Colocation discovery
The Meta Quest Colocation Discovery feature enables your app to discover other physically colocated devices running the same app. Colocation Discovery simplifies the process of users joining a networked session when in the same physical space, and removes the need for users of your app to manually find lobbies or share join codes.
The following sections describe how to advertise and discover messages between colocated users of your app.
Enable colocation discovery
To enable Meta Quest Colocation Discovery in your app:
- Go to Project Settings > XR Plug-in Management > OpenXR.
- Under OpenXR Feature Groups, select the Meta Quest feature group.
- If disabled, enable the Meta Quest: Colocation Discovery OpenXR feature.
Check support
The colocation discovery feature is enabled on supported devices and disabled on unsupported devices. The following example code checks whether the feature is supported on a target device by checking whether the feature is enabled:
bool IsColocationDiscoverySupported()
{
var settings = OpenXRSettings.Instance;
var feature = settings.GetFeature<ColocationDiscoveryFeature>();
return feature.enabled;
}
Colocation advertisement
Colocation advertisement is the process of broadcasting a message for colocated users to discover. For example you might broadcast connection details such as your devices local IP address to facilitate colocated users connecting to a networked session. You might also broadcast a shared anchor's group ID for users to synchronize reference points in your space.
Note
Colocation advertisement should not be used as an alternative to traditional networking frameworks, such as Netcode for GameObjects, for creating multiplayer experiences. It is intended to simplify the initial connection process for colocated users to get into a networked session.
Start advertisement
When you start colocation advertisement, the advertisementState transitions between Inactive
, Starting
, and Active
. You can be notified when the advertisement state changes by subscribing to ColocationDiscoveryFeature.advertisementStateChanged. You can query the current advertisement state via ColocationDiscoveryFeature.advertisementState
. During the transition period when the state is Starting
can fail and return back to Inactive
. If the advertisementState
is not Inactive
when calling TryStartAdvertisementAsync
, the request will fail with XRResultStatus.StatusCode.ValidationFailure.
To start colocation advertisement, call ColocationDiscoveryFeature.TryStartAdvertisementAsync as shown in the following code example:
async void StartAdvertisementAsync(
ColocationDiscoveryFeature colocationDiscovery,
byte[] message)
{
// First subscribe to state changes to know when advertisement is active and
// when it might change to inactive if the runtime stops advertisement without
// app request.
colocationDiscovery.advertisementStateChanged += OnAdvertisementStateChanged;
var result = await colocationDiscovery.TryStartAdvertisementAsync(
message.AsSpan());
if (result.status.IsSuccess())
{
// Advertisement has started and is active
// The ID of the advertisement that was started
var advertisementId = result.value;
}
}
void OnAdvertisementStateChanged(object sender, Result<ColocationState> result)
{
var state = result.value;
switch (state)
{
case ColocationState.Starting:
// Advertisement is trying to start
break;
case ColocationState.Active:
// Advertisement is active
break;
case ColocationState.Stopping:
// Advertisement is trying to stop
break;
case ColocationState.Inactive:
// Advertisement is inactive.
break;
}
}
Stop advertisement
When you stop colocation advertisement, the advertisementState
transitions between Active
, Stopping
, and Inactive
. The transition period when the state is Stopping
can fail and return back to Active
. If the advertisementState
is not Active
when calling TryStopAdvertisementAsync
, the request will fail with XRResultStatus.StatusCode.ValidationFailure
.
To stop colocation advertisement, call ColocationDiscoveryFeature.TryStopAdvertisementAsync as shown in the following code example:
async void StopAdvertisementAsync(ColocationDiscoveryFeature colocationDiscovery)
{
var resultStatus = await colocationDiscovery.TryStopAdvertisementAsync();
if (resultStatus.IsSuccess())
{
// Advertisement has stopped and is inactive
}
}
The OpenXR runtime can also stop advertisement on its own without a request to stop it from your app. When the runtime stops advertisement, it invokes ColocationDiscoveryFeature.advertisementStateChanged
and sets the ColocationState
to ColocationState.Inactive
. If discovery stopped and you did not request it to stop, you can check the result passed with the event for an error with Result.status.IsError() and inspect the Result.status.nativeStatusCode to get the native error status code.
Advertisement message
Colocation advertisement takes a Span<byte>
message type to broadcast information to colocated users actively engaged in Colocation discovery. The OpenXR runtime makes a deep copy of this Span<byte>
and therefore does not require the data to persist after the call to ColocationDiscoveryFeature.TryStartAdvertisementAsync
.
Note
The max size of an advertisement message is 1024 bytes.
If you want to share a SerializableGuid
, such as the MetaOpenXRAnchorSubystem.sharedAnchorsGroupId, you can get a NativeArray<byte>
with SerializableGuid.AsByteNativeArray and pass it with NativeArray.AsSpan()
as shown in the following code example:
async void AdvertiseSerializableGuid(
ColocationDiscoveryFeature colocationDiscovery,
SerializableGuid guid)
{
var bytes = guid.AsByteNativeArray();
var result = await colocationDiscovery.TryStartAdvertisementAsync(
bytes.AsSpan());
if (result.status.IsSuccess())
{
// Advertisement started and is active
}
}
You can convert a string
to a byte array and share it as shown in the following code example:
async void AdvertiseString(
ColocationDiscoveryFeature colocationDiscovery,
string message)
{
var bytes = Encoding.ASCII.GetBytes(message);
var result = await colocationDiscovery.TryStartAdvertisementAsync(
bytes.AsSpan());
if (result.status.IsSuccess())
{
// Advertisement has started and is active
}
}
Note
This example uses ASCII
for the encoding type but you can use an alternative type that suits your needs.
Colocation discovery
Colocation discovery is the process of listening for advertised messages from colocated users of your app. For example, someone might broadcast connection details such as an IP address to facilitate users to join a networked session.
Start discovery
When you start colocation discovery, the discoveryState transitions between Inactive
, Starting
, and Active
. You can be notified when the discovery state changes by subscribing to ColocationDiscoveryFeature.discoveryStateChanged. You can query the current discovery state via ColocationDiscoveryFeature.discoveryState
. During the transition period when the state is Starting
can fail and return back to Inactive
. If the discoveryState
is not Inactive
when calling TryStartDiscoveryAsync
, the request will fail with XRResultStatus.StatusCode.ValidationFailure
.
To start colocation discovery, call ColocationDiscoveryFeature.TryStartDiscoveryAsync as shown in the following code example:
async void StartDiscoveryAsync(ColocationDiscoveryFeature colocationDiscovery)
{
// First subscribe to state changes to know when discovery is active and
// when it might change to inactive if the runtime stops discovery without
// app request.
colocationDiscovery.discoveryStateChanged += OnDiscoveryStateChanged;
// Subscribe to message discovery events
colocationDiscovery.messageDiscovered += OnMessageDiscovered;
var resultStatus = await colocationDiscovery.TryStartDiscoveryAsync();
if (resultStatus.IsSuccess())
{
// Discovery has started and is active
}
}
void OnDiscoveryStateChanged(object sender, Result<ColocationState> result)
{
var state = result.value;
switch (state)
{
case ColocationState.Starting:
// Discovery is about to start
break;
case ColocationState.Active:
// Discovery is active
break;
case ColocationState.Stopping:
// Discovery is about to stop
break;
case ColocationState.Inactive:
// Discovery is inactive. Inspect result.status if your app did not
// request discovery to stop to learn why the runtime stopped
// discovery.
break;
}
}
void OnMessageDiscovered(object sender, ColocationDiscoveryMessage message)
{
// Convert message from bytes back to the structure it was advertised as.
}
Stop discovery
When you stop colocation discovery, the discoveryState
transitions between Active
, Stopping
, and Inactive
. During the transition period when the state is Stopping
can fail and return back to Active
. If the discoveryState
is not Active
when calling TryStopDiscoveryAsync
, the request will fail with XRResultStatus.StatusCode.ValidationFailure
.
To stop colocation discovery, call ColocationDiscoveryFeature.TryStopDiscoveryAsync as shown in the following code example:
async void StopDiscoveryAsync(ColocationDiscoveryFeature colocationDiscovery)
{
var resultStatus = await colocationDiscovery.TryStopDiscoveryAsync();
if (resultStatus.IsSuccess())
{
// Discovery has stopped and is inactive
}
}
The OpenXR runtime can also stop discovery on its own without a request to stop it from your app. When the runtime stops discovery, it invokes ColocationDiscoveryFeature.discoveryStateChanged
and sets the ColocationState
to ColocationState.Inactive
. If discovery stopped and you did not request it to stop, you can check the result passed with the event for an error with Result.status.IsError() and inspect the Result.status.nativeStatusCode to get the native error status code.
Discovered messages
Subscribe to ColocationDiscoveryFeature.messageDiscovered to be notified when a message is discovered. The messageDiscovered
event passes a XRColocationDiscoveredMessage that contains a SerializableGuid
representing the advertisement ID and NativeArray<byte>
representing the data of the message. The NativeArray<byte>
is allocated with Allocator.Temp
and will be disposed of at the end of the frame. The advertisement ID is used to distinguish between different advertised messages. The data contains the message the advertiser is broadcasting.
If the advertised message is a SerializableGuid
, you can convert the message of bytes back to a SerializableGuid
by passing the bytes to a constructor of a new Guid
and passing that guid into the constructor of a new SerializableGuid
. The following code example demonstrates how to convert bytes to a SerializableGuid
:
void ConvertBytesToSerializableGuid(NativeArray<byte> bytes)
{
var guid = new Guid(bytes);
var serializedGuid = new SerializableGuid(guid);
}
If the advertised message is a string, you can convert the message of bytes back to a string by using Encoding.GetString
as shown in the following code example:
void ConvertBytesToString(NativeArray<byte> bytes)
{
var message = Encoding.ASCII.GetString(bytes);
}
Note
The encoding type must match the encoding type that is used when converting the string to bytes from colocation advertisement.