Developer Guide
Version 1.16
2 | Introduction | Oculus Platform
Copyrights and Trademarks
©
2017 Oculus VR, LLC. All Rights Reserved.
OCULUS VR, OCULUS, and RIFT are trademarks of Oculus VR, LLC. (C) Oculus VR, LLC. All rights reserved.
BLUETOOTH is a registered trademark of Bluetooth SIG, Inc. All other trademarks are the property of their
respective owners. Certain materials included in this publication are reprinted with the permission of the
copyright holder.
2 | | Oculus Platform | Contents | 3
Contents
Achievements....................................................................................................... 4
Cloud Storage.................................................................................................... 13
Coordinated App Launch (CAL)........................................................................ 18
Discoverability.................................................................................................... 20
Announcements............................................................................................................................................... 21
Events............................................................................................................................................................... 22
In-App Content................................................................................................................................................ 24
Commerce (IAP)................................................................................................. 26
Leaderboards..................................................................................................... 31
Matchmaking...................................................................................................... 37
Configuration Overview................................................................................................................................... 37
SDK Overview.................................................................................................................................................. 41
Matchmaking Quickstart w/Advanced Options...............................................................................................42
Adding Skill and Using Queries...................................................................................................................... 45
Testing and Tuning..........................................................................................................................................48
Peer-to-Peer Networking................................................................................... 50
Parties................................................................................................................. 56
Rooms.................................................................................................................58
Sharing............................................................................................................... 64
User and Friends................................................................................................66
Voice Chat (VoIP)............................................................................................... 67
User Verification................................................................................................. 74
4 | Achievements | Oculus Platform
Achievements
Create trophies, badges, awards, and more to challenge your users to reach a goal or objective. Users can see
the achievements their friends have earned creating a competition among friends.
Achievements earned in your app can also be displayed in Oculus Home to show a user's progress in and
progression through a game.
This guide will walk you through how to define your global achievements, the available SDK methods
and Server-to-Server calls you can make to interact with the achievements service, and an example Unity
implementation you can review.
The Oculus Platform supports three achievement types: simple, count, and bitfield. Each achievement type has
a different unlock mechanism.
• ‘Simple’ achievements are all-or-nothing. They are unlocked by a single event or objective completion. E.g.
achievement is unlocked when Frodo reaches Mount Doom.
• ‘Count’ achievements are unlocked when a counter reaches a defined target. Define the ‘Target’ to reach
for triggering the achievement. E.g. achievement is unlocked when Darth Vader chokes 3 disappointing
Imperial officers.
• ‘Bitfield’ achievement is unlocked when a target number of bits in a bitfield are set. Define the ‘Target’ and
‘Bitfield Length’ for triggering the achievement. E.g. achievement is unlocked when Harry destroys 5 of the 7
Horcruxes.
The Oculus Platform will track and manage these achievements. However, your app will manage updating/
triggering achievements and displaying them to your users.
Create Your Achievements
The first step in adding achievements to your game is to define the achievements and how they will be
unlocked. To create an achievement go the Achievements tab on the Developer Dashboard.
When creating achievements, you can choose to localize the achievement into multiple languages. When
entering the achievement information, select 'Choose Languages', check the boxes of the languages you would
like to localize into, and enter the information for the languages selected. The language displayed to the user is
based on the locale set for the user's device OS.
Select ‘Create Achievement’ and enter the following information:
• API Name - The unique string that you use to reference the achievement in your app.
• Title - The short descriptive name that the user will see.
• Description - The full description of the achievement. You may wish to describe how a user can unlock or
earn this achievement.
• Unlocked Description - (Optional) This is a description that will replace the 'Description' after the user has
unlocked the achievement.
• Locked and Unlocked Icons - (Optional) The icons associated with the achievement. The ‘Locked Icon’ will
be displayed to users who have not earned the achievement, while the ‘Unlocked Icon’ will be displayed to
the users who have. If only an ‘Unlocked Icon’ is provided, the ‘Locked Icon’ will be a grayscale version of
the ‘Unlocked Icon’. If neither is provided, a default icon will be used.
• Write Policy - Choose one of the two Write Policy options:
• ‘Client Authoritative’ is the default setting and means that achievement progress may be written or
unlocked from the client app.
Oculus Platform | Achievements | 5
• ‘Server Authoritative’ means that the achievement can only be written or updated using Server-to-Server
APIs listed below. This is typically used to reduce cheating when trusted servers are running the game
sessions. Achievement information and progress may still be queried from the client app.
• Is Achievement Secret - Yes/No toggle that chooses whether the achievement title, description, icon, and
progress will be hidden until the achievement is completely earned or unlocked. Default selection is 'No'.
• Type - This is the type of achievement. The values are ‘simple’, ’count’, and ’bitfield’. See the description
above for information about these achievement types.
Select ‘Submit’ when you’re finished to save the achievement. You can update your achievements in the
Developer Center at any time.
Archiving Achievements
You can, at any time, archive an achievement for an app. Archiving does not delete the achievement or a user's
progress, it hides the achievement and any progress the user. You can unarchive an achievement to restore
visibility to users.
Integrating Achievements
Once you’re finished creating the achievements, you can begin to integrate them in your game. When calling
the SDK methods in this section use the ‘API Name’ you defined in the Developer Center.
The following SDK methods can be called from your client app.
• Retrieve information about an achievement:
Native - ovr_Achievements_GetDefinitionsByName()
Unity - Platform.Achievements.GetDefinitionsByName()
Review the ovr_Achievements_GetDefinitionsByName page for information about the parameters and return
data structure.
• Retrieve information about the user’s progress on an achievement:
Native - ovr_Achievements_GetProgressByName()
Unity - Platform.Achievements.GetProgressByName()
Review the ovr_Achievements_GetProgressByName page for information about the parameters and return
data structure.
• Retrieve information about all achievements:
Native - ovr_Achievements_GetAllDefinitions()
Unity - Platform.Achievements.GetAllDefinitions()
Review the ovr_Achievements_GetAllDefinitions page for information about the parameters and return data
structure.
• Retrieve information about the user’s progress on all achievements:
Native - ovr_Achievements_GetAllProgress()
Unity - Platform.Achievements.GetAllProgress()
Review the ovr_Achievements_GetAllProgress page for information about the parameters and return data
structure.
The following SDK methods can be called for any achievement that has a ‘Client Authoritative’ Write Policy.
If the achievement is ‘Server Authoritative’ you’ll need to use the S2S REST calls in the section below to make
updates from your trusted server.
6 | Achievements | Oculus Platform
• Unlock an achievement:
Native - ovr_Achievements_Unlock()
Unity - Platform.Achievements.Unlock()
Review the ovr_Achievements_Unlock page for information about the parameters and return data structure.
This will completely unlock an achievement, including Count and Bitfield, even if the target has not been
met.
• Increment the count on a 'Count' achievement:
Native - ovr_Achievements_AddCount()
Unity - Platform.Achievements.AddCount()
Review the ovr_Achievements_AddCount page for information about the parameters and return data
structure.
• Add to a bitfield achievement:
Native - ovr_Achievements_AddFields()
Unity - Platform.Achievements.AddFields()
Review the ovr_Achievements_AddFields page for information about the parameters and return data
structure. Once a bit is “unlocked” it will not change from that state, e.g. if the bitfield is 10011 and an
AddFields call is made with 00110, the resulting state is 10111.
Example Implementation
The following Unity example demonstrates setting an achievement to unlock on an event that you define.
The following example is taken from the VRHoops sample app. Please see the Sample Apps page for more
information about the apps that are available.
First the example defines an achievement you configured on the Developer Center called 'LIKES_TO_WIN'.
The example then checks for an update message to see if the achievement has been unlocked and, if true,
sets the achievement as unlocked in the app. Otherwise, the game moves on and increments the count on the
achievement if a game condition is met, in this example if a win is recorded.
using
using
using
using
UnityEngine;
System.Collections;
Oculus.Platform;
Oculus.Platform.Models;
public class AchievementsManager : MonoBehaviour
{
// API NAME defined on the dashboard for the achievement
private const string LIKES_TO_WIN = "LIKES_TO_WIN";
// true if the local user hit the achievement Count setup on the dashboard
private bool m_likesToWinUnlocked;
public bool LikesToWin
{
get { return m_likesToWinUnlocked; }
}
public void CheckForAchievmentUpdates()
{
Achievements.GetProgressByName(new string[]{ LIKES_TO_WIN }).OnComplete(
(Message<AchievementProgressList> msg) =>
{
foreach (var achievement in msg.Data)
{
if (achievement.Name == LIKES_TO_WIN)
{
m_likesToWinUnlocked = achievement.IsUnlocked;
}
}
Oculus Platform | Achievements | 7
}
}
);
}
public void RecordWinForLocalUser()
{
Achievements.AddCount(LIKES_TO_WIN, 1);
CheckForAchievmentUpdates();
}
S2S REST Requests
Certain actions require to you to interact directly with our server. For example, if any achievement is set
to ‘Server Authoritative’ you’ll need to make API calls from your trusted server to increment and unlock
achievements. See the Server-to-Server Basics page for information about interacting with our APIs.
Create or Update an Achievement (POST)
Create or update an achievement allows you to create a new achievement, or update one that already exists.
This will update the achievement for all users.
Parameter
Required Y/N
Description
Type
Example
api_name
Y
The name used to
reference to the
achievement in
this API and in the
client SDK. This
alphanumeric string
must be unique for
the app.
string
“VISIT_3_CONTINENTS”
This is the
achievement
type. There are
three types of
achievement,
please see the
description above
for information on
the different types.
enum
"simple"
This determines
who is allowed to
write achievement
progress. Please
see the description
above for
information on the
two different write
policies.
enum
The number of
event occurrences
for the achievement
to be unlocked.
Please see the
integer
achievement_type Y
entry_write_policy
Y
target
Y, if
achievement_type
= count or
bitfield
• simple
• count
• bitfield
"client_authoritative"
• client_
authoritative
• server_
authoritative
50
8 | Achievements | Oculus Platform
Parameter
Required Y/N
Description
Type
Example
integer
7
description above
for more information
on target.
bitfield_length Only if
The size of the
achievement_type bitfield for this
= bitfield
achievement.
is_archived
N - Default is
false.
Boolean that
boolean
determines if the
achievement is
archived. Can
also be used
to unarchive an
achievement.
Archiving does
not delete the
achievement or user
progress.
title
Y
The name of the
achievement that
the user sees.
string
description
Y
The text description
that the user sees.
string
The text description
that the user
sees when the
achievement is
unlocked.
string
unlocked_description
N
_override
is_secret
N - Default is
false.
unlocked_image_file
N - A default image
will be used.
locked_image_fileN - If an unlocked
image is provided,
a grayscale version
will be used as
the locked image.
Boolean that
boolean
chooses whether
the achievement is
hidden until earned.
The local path to
string
the icon shown after
the achievement has
been earned. Must
be a 256x256 PNG
file.
The local path to the string
icon shown before
the achievement is
"false"
"Visited 3
Continents"
"This
achievement
unlocks
when..."
"Congratulations!
You visited 3
continents."
"false"
“@/path/to/
unlocked_icon.png;
type=image/png”
“@/path/to/
locked_icon.png;
type=image/png”
Oculus Platform | Achievements | 9
Parameter
Required Y/N
Description
Otherwise a default
is used.
earned. Must be a
256x256 PNG file.
Type
Example
Example Create/Update Request
$ curl -F "access_token=$APP_ACCESSTOKEN" -F "api_name=VISIT_3_CONTINENTS"
-F "achievement_type=BITFIELD" -F "achievement_write_policy=CLIENT_AUTHORITATIVE"
-F "target=3" -F "bitfield_length=7" -F "is_archived=false" -F "title=Achievement Title"
-F "description=How to earn me" -F "unlocked_description_override=You did it"
-F "is_secret=false" -F "locked_image_file=@/path/to/locked_icon.png;type=image/png"
-F "unlocked_image_file=@/path/to/unlocked_icon.png;type=image/png"
https://graph.oculus.com/$APPID/achievement_definitions
Example Response
{
"id":"1074233745960170"
}
Retrieve Achievement Definitions (GET)
Query achievement definitions allows you to get information about achievements to display to your users.
Parameter
Required Y/N
Description
Type
Example
api_names
N
The names of
the achievement
definitions to
fetch. If omitted
all achievement
definitions are
returned.
array of strings
["VISIT_3_CONTINENTS",
"WALK_500_MILES"]
include_archived N
Boolean whether
boolean
to include archived
achievements. This
may only be used
when an App Access
Token is used to
authenticate.
"false"
Example Request
$ curl -G -d "access_token=$APP_ACCESSTOKEN|$USER_ACCESSTOKEN"
-d 'api_names=["VISIT_3_CONTINENTS", "WALK_500_MILES"]' -d "include_archived=true"
-d
'fields=api_name,achievement_type,achievement_write_policy,target,bitfield_length,is_archived,title,
description,unlocked_description_override,is_secret,locked_image_uri,unlocked_image_uri'
https://graph.oculus.com/$APPID/achievement_definitions
Note: The example above shows "access_token=$APP_ACCESSTOKEN|$USER_ACCESSTOKEN",
this is showing that you can use either the User Access Token or the App Access Token and retrieve the
same result. It is not a combination of the two tokens.
The definition of the ‘fields’ are the same as in the create or update API call above. Note that the
images are locked_image_uri and unlocked_image_uri and notlocked_image_file and
unlocked_image_file. This is the location of the image source, and not the location of the local image file.
10 | Achievements | Oculus Platform
Example Response
{
"data":
[
{
"id":"1074233745960170",
"api_name":"VISIT_3_CONTINENTS",
"achievement_type":"BITFIELD",
"achievement_write_policy":"CLIENT_AUTHORITATIVE",
"target":3, "bitfield_length":7,
"is_archived":false,
"title":"Achievement Title",
"description":"How to earn me",
"unlocked_description_override":"You did it",
"is_secret":false,
"locked_image_uri":"https://scontent.oculuscdn.com/...",
"unlocked_image_uri":"https://scontent.oculuscdn.com/..."
},
{
...
}
]
}
Write (and Unlock) Achievement Progress (POST)
Write achievement progress updates a user’s progress on an achievement. This method accumulates progress,
for count type achievements, instead of overwriting values. For example, "add_count=25" will increment the
count by 25, not set the current count to 25. This is so that conflicts that arise from updating achievements
from multiple sources simultaneously or making progress from multiple devices in offline mode can be handled
gracefully.
Parameter
Required Y/N
Description
Type
Example
api_name
Y
The names of the
achievement to
update.
string
"VISIT_3_CONTINENTS"
Y if the achievement Value to add
is a ‘Count’ type.
to the progress
counter for this
achievement. Only
valid for COUNT
achievements.
integer
25
Y if the achievement Bits to add to the
is a ‘Bitfield’ type.
progress of this
achievement. Only
valid for BITFIELD
achievements.
string
"110001"
N - Default is “false” Instantly unlocks
an achievement
regardless of
progress. This
must be used to
unlock SIMPLE
achievements.
boolean
"false"
add_count
add_bits
force_unlock
Oculus Platform | Achievements | 11
Example Request
$ curl -d "access_token=$APP_ACCESSTOKEN|$USER_ACCESSTOKEN" -d "api_name=MY_ACHIEVEMENT"
-d "add_bits=0011001" -d "add_count=25" -d "force_unlock=true"
https://graph.oculus.com/$USERID/achievements
Note: The example above shows "access_token=$APP_ACCESSTOKEN|$USER_ACCESSTOKEN",
this is showing that you can use either the User Access Token or the App Access Token and retrieve the
same result. It is not a combination of the two tokens.
Example Response
{
"id":"1074233745960170",
"api_name":"MY_ACHIEVEMENT",
"just_unlocked":true
}
The response will contain the parameter ‘just_unlocked’ that indicates if this operation caused the
achievement to unlock.
Query Achievement Progress (GET)
Retrieve a user’s progress for an achievement.
Parameter
Required Y/N
Description
Type
Example
api_names
N
The names of
the achievement
definitions to
fetch. If omitted
all achievement
definitions are
returned.
array of strings
["VISIT_3_CONTINENTS",
"WALK_500_MILES"]
Example Request
The definitions for the ‘fields’ are the same as in the Create or Update Achievement call above.
$ curl -G -d "access_token=$APP_ACCESSTOKEN|$USER_ACCESSTOKEN" -d 'api_names=["VISIT_3_CONTINENTS",
"WALK_500_MILES"]' -d
'fields'='definition{api_name,target},count_progress,bitfield_progress,is_unlocked,unlock_time'
https://graph.oculus.com/$USERID/achievements
Note: The example above shows "access_token=$APP_ACCESSTOKEN|$USER_ACCESSTOKEN",
this is showing that you can use either the User Access Token or the App Access Token and retrieve the
same result. It is not a combination of the two tokens.
Example Response - The following is an example of an unlocked bitfield achievement.
{
"data": [
{
"id": "1074233745960170",
"definition": {
"api_name": "VISIT_3_CONTINENTS",
"target": 3
},
"count_progress": 0,
"bitfield_progress": "1001100",
"is_unlocked": true,
"unlock_time": 1459815726
}
]
12 | Achievements | Oculus Platform
}
Remove all Achievements and Progress for a User (POST)
This method will remove all achievement progress, both locked and unlocked, for a user.
$ curl -d "access_token=$APP_ACCESSTOKEN" -d "user_id=$USERID"
https://graph.oculus.com/achievement_remove_all
Example Response
{
"success":true
}
Oculus Platform | Cloud Storage | 13
Cloud Storage
Seamlessly save, synchronize, and load data between devices and installs using our Cloud Storage service.
Cloud Storage is designed to support the following use-cases:
1. Save progress between installs. Users are able to uninstall and re-install without losing their saved data.
2. Share progress between devices. Users can sync data between user devices.
3. Disaster recovery. User data can be restored if devices are lost or damaged, or if the local data is corrupted.
Storage Formats
The Cloud Storage service uses buckets (directories) and blobs (files), indexed by a key (file name), to store
data. The number of blobs stored per Bucket can not exceed 10,000. However, apps should use a smaller limit
for the number of blobs, as the resources required to process a large number of keys may impact the user’s
experience.
Direct file storage is not supported.
Note: The Cloud Storage service does not retain old versions of data. An app that needs old save
files preserved should store them using a Bucket/Key backup strategy. For example, creating buckets
saved_games_prior1 and saved_games_prior2 would allow to you store 2 old save files in
addition to the latest save.
Data Storage Structure
Cloud Storage is divided into buckets (directories) of data that are configured in the Developer Center, under
the Cloud Storage tab. Apps may define more than one bucket for multiple save types or data.
For example, when creating a new save you would call ovr_CloudStorage_Save(bucket_name,
key, data_pointer, data_size, counter, extra_data). More details can be found in the
ovr_CloudStorage_Save reference.
bucket_name - The name defined in the Developer Center.
key - The unique index for the blob. Total size cannot exceed 512 bytes.
data_pointer - The pointer to the data blob in your app.
data_size - The size, in bytes, of the data blob.
counter - The counter is an incremented uint64 metadata. The value is optional unless the Highest Counter
conflict resolution policy is specified.
• extra_data - Any extra, optional, metadata. Total size cannot exceed 512 bytes as a unsigned 64bit
integer data.
•
•
•
•
•
Creating Buckets
In the Cloud Storage tab of the Developer Center click the ‘Create Cloud Storage Bucket’ button and enter the
information about the bucket that you would like to create.
Bucket names must to conform to the Microsoft Windows directory name restrictions (https://
msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx), are case-insensitive, must be
unique within an Application Grouping, and less than 128 characters. Buckets do not exist by default for an
application, to enable Cloud Storage at least one must be created on the dashboard.
Information about conflict management strategies can be found below in the Managing Conflicts section
below.
14 | Cloud Storage | Oculus Platform
Integrating Cloud Storage
Once you've defined the Cloud Storage buckets, you can integrate the service into your game. When calling
the methods listed below you'll reference the bucket_name you created for the bucket.
The following is the typical Cloud Storage lifecycle:
1. Retrieve a list of the entries in each Cloud Storage bucket you've defined by calling
ovr_CloudStorage_LoadBucketMetadata(). Perform any conflict resolution required if the data
retrieved is not in sync with the local data. Information about how to resolve conflicts can be found later in
this guide.
2. Once the metadata has been loaded and you've determined which save data you need to load, call
ovr_CloudStorage_Load(), with the data key, to load the data. You can now launch your game using
the game state data you loaded.
3. During gameplay and at the prompt of the user, save the game state data by calling
ovr_CloudStorage_Save() with the data blob. We recommend triggering saves once a minute, at most,
and when prompted to prevent runaway game saves.
4. When quitting the game, make sure to reconcile local and cloud saves ensuring that the latest data is saved
to the Cloud Storage service.
Note: We recommend that you review the sample app described below for a detailed walk-through of
how you may implement the Cloud Storage service.
The following SDK methods can be called from your client app:
• Save a blob:
Native - ovr_CloudStorage_Save()
Unity - Platform.CloudStorage.Save()
Data is stored as an opaque binary blob, review the ovr_CloudStorage_Save page for information about the
parameters and return data structure.
This call sends the blob data to the locally running Oculus process and will not trigger network calls.
Network synchronization happens when the App is no longer running. Total size for a blob can not exceed
10Mb.
Note: It is important, when sending a cloud save, that a local copy of the data is maintained until
confirmation is received that the cloud save was successful. If an error occurs, the data contained in
the request will be lost and you'll need to try again.
• Load a blob:
Native - ovr_CloudStorage_Load()
Unity - Platform.CloudStorage.Load()
Data is stored as an opaque binary blob, review the ovr_CloudStorage_Load page for information about the
parameters and return data structure.
If the bucket is configured for manual conflict resolution and a conflict exists between the local and remote
versions, the load call will return an error. The process to resolve conflicts is discussed below. Loading a
blob will not initiate a network call.
• Delete a blob:
Native - ovr_CloudStorage_Delete()
Unity - Platform.CloudStorage.Delete()
Oculus Platform | Cloud Storage | 15
Data is stored as an opaque binary Blob, review the ovr_CloudStorage_Delete page for information about
the parameters and return data structure.
This will delete both the cloud and local copies of the blob. You may write new data to a deleted blob
without waiting for synchronization.
• Find a blob:
You may want to retrieve some information about the available blobs to determine which one to load. The
following methods allow you to retrieve the metadata for one or all saved data blobs.
• Retrieve blob metadata for all blobs:
Native - ovr_CloudStorage_LoadBucketMetadata(bucket_name)
Unity - Platform.CloudStorage.LoadBucketMetadata(bucket_name)
Review the ovr_CloudStorage_LoadBucketMetadata page for information about the parameters and
return data structure.
• Retrieve blob metadata for a specific blob:
Native - ovr_CloudStorage_LoadMetadata(bucket_name, key)
Unity - Platform.CloudStorage.LoadMetadata(bucket_name, key)
Review the ovr_CloudStorage_LoadMetadata page for information about the parameters and return data
structure.
Blob Metadata
Both of the metadata SDK methods will return results in the same format containing information you can use to
determine which save you want to use or load.
The response payload will be in a format similar to the following:
uint32 data_size = ovr_CloudStorageMetadata_getDataSize(metadataHandle)
uint64 saved_time = ovr_CloudStorageMetadata_getSaveTime(metadataHandle)
int64 counter = ovr_CloudStorageMetadata_getCounter(metadataHandle)
const char* extra_data = ovr_CloudStorageMetadata_getExtraData(metadataHandle)
ovrCloudStorageVersionHandle handle = ovr_CloudStorageMetadata_getHandle(metadataHandle)
ovrCloudStorageDataStatus status = ovr_CloudStorageMetadata_getStatus(metadataHandle)
• data_size - The size, in bytes, of the stored data Blob.
• saved_time - the UTC time, in seconds, since the UNIX epoch when the blob was locally saved. This is the
time as recorded on the local device so includes local clock skew.
• counter - The value specified when saved, else zero.
• extra_data - the value specified when saved, else NULL.
• handle - used for manual conflict resolution (see below).
• status - enum describing the state of the Blob. This state as determined by the Oculus process's most
recent network update. The following states are possible:
• ovrCloudStorageDataStatus_InSync - the local and remote versions are in-sync.
• ovrCloudStorageDataStatus_NeedsDownload - a newer version exists in the cloud but hasn't yet
downloaded.
• ovrCloudStorageDataStatus_NeedsUpload - the local version is newer and needs to be uploaded.
• ovrCloudStorageDataStatus_InConflict - the local and remote version have a conflict that must
be manually resolved. Only occurs for buckets set to manual conflict resolution.
16 | Cloud Storage | Oculus Platform
Managing Conflicts
The Cloud Storage service supports synchronizing data between multiple devices and platforms. This requires a
conflict resolution strategy to handle situations where multiple devices may upload blobs to the same key. This
situation is common on mobile devices but can occur between PCs as well. The following resolution strategies
are available:
Latest Timestamp
Resolving the conflict by using the latest timestamp is the least complex resolution. It configures the bucket to
prefer the blob that has the timestamp recorded most recently by the local device. A timestamp is recorded
at the time the call to ovr_CloudStorage_Save() is made. Client blobs with timestamps earlier than the
remotely stored version are discarded.
Highest Counter
Buckets configured with the highest counter method will prefer blobs with the highest value set in the counter
field. This method could be used if you wanted to preserve the blob with the highest score. Blobs stored with
the same counter value as a remote version will attempt to be stored. However, multiple devices doing this
represent a race-condition where either device may win.
Manual
You may also choose to handle conflict resolution entirely in your app. When an app saves a new local blob to a
specific key that's in state ovrCloudStorageDataStatus_InConflict, the blob will not be uploaded until
the app resolves the conflict. Conflict resolution can be done at any time but it's best done during App startup
and shutdown. The need for manual conflict resolution can be detected by checking the metadata status, or by
reviewing the save message response.
ovrCloudStorageUpdateResponseHandle response =
ovr_Message_GetCloudStorageUpdateResponse(save_message)
ovrCloudStorageUpdateStatus status = ovr_CloudStorageUpdateResponse_GetStatus(response))
if (status == ovrCloudStorageUpdateStatus_ManualMergeRequired) { // perform manual merge... }
The first step to resolving is to load the metadata for the conflicting blobs:
ovr_CloudStorage_LoadConflictMetadata(bucket, key)
This is an asynchronous call and the response message is parsed to get the metadata:
ovrCloudStorageConflictMetadataHandle response =
ovr_Message_GetCloudStorageConflictMetadata(message)
ovrCloudStorageMetadataHandle local_metadata = ovr_CloudStorageConflictMetadata_GetLocal(response)
ovrCloudStorageMetadataHandle remote_metadata = ovr_CloudStorageConflictMetadata_GetRemote(response)
• Resolving based on metadata
If the metadata for the blob contains enough information to resolve the conflict, then the next step is to call:
ovr_CloudStorage_ResolveKeepRemote(bucket_name, key, remote_handle)
to choose the remote blob, or:
ovr_CloudStorage_ResolveKeepLocal(bucket_name, key, remote_handle)
to choose the local blob. The handle from the remote blob's metadata is passed in to prevent data loss in
the instance that a new remote blob appears during conflict resolution.
• Resolving by inspecting data
Oculus Platform | Cloud Storage | 17
If you need to inspect the data blobs to determine which to keep, they can be loaded using the handle from
the metadata:
ovr_CloudStorage_LoadHandle(handle)
This works for both the local and remote handles. If the remote blob has not been cached locally, requesting
the remote will initiate a network call to fetch any remaining data.
• Resolving by merging the remote and local data
If the App needs to merge the saved blobs, they can be loaded as described above, then the app can save
a new local version and resolve by choosing that:
ovr_CloudStorage_Save(bucket_name, key, merged_data_pointer, merged_data_size, counter,
extra_data)
ovr_CloudStorage_ResolveKeepLocal(bucket_name, key, remote_handle)
Example Implementation
The CloudStorageSample Unity app provided in the Platform SDK download demonstrates using the Cloud
Storage service to save multiple records and perform conflict resolution. Please see the Sample Apps page for
more information about the apps that are available.
18 | Coordinated App Launch (CAL) | Oculus Platform
Coordinated App Launch (CAL)
Coordinated App Launch (CAL) allows users to form groups and launch your social app together from Oculus
Home.
There are two steps to using CAL in your game, integrating the CAL API and configuring CAL on the Oculus
Developer Dashboard.
Coordinated App Launch is only available for Gear VR apps at this time.
Integrate CAL API
When users launch a CAL-enabled app from Rooms, your app will receive information about the users in the
room, as well as the unique roomID created for the session. This information is sent with the launch intent. You
can also retrieve this information from the following method:
ovrLaunchDetailsHandle handle = ovr_ApplicationLifecycle_GetLaunchDetails();
if (ovr_LaunchDetails_GetLaunchType(handle) == ovrLaunchType_Coordinated) {
ovrID roomID = ovr_LaunchDetails_GetRoomID(handle);
ovrUserArrayHandle usersHandle = ovr_LaunchDetails_GetUsers(handle);
...
}
Or if you're using Unity:
using Oculus.Platform;
using Oculus.Platform.Models;
LaunchDetails launchDetails =
LaunchDetails(CAPI.ovr_ApplicationLifecycle_GetLaunchDetails());
if (launchDetails.LaunchType == LaunchType.Coordinated) {
UInt64 roomID = launchDetails.RoomID;
UserList users = launchDetails.Users;
...
}
These methods are synchronous and will return results immediately.
Note: To test your integration during development, you can simulate a CAL event by using the
following command on your phone:
adb shell "am start -n com.your.package.name/.YourMainActivity -e intent_cmd \
'"'{"ovr_social_launch":{"type":"COORDINATED","room_id":"123","users" \
:[{"id":"4","alias":"janedoe1234"},{"id":"7","alias":"johndoe5678" \
}]}}'"'"
Replacing com.your.package.name/.YourMainActivity with your app's package name and initial activity,
and the 'users' array with the IDs and user names of your own test users.
You can use this CAL information to place the group in a shared VR session.
If you plan to use Voice Chat (VoIP) on page 67 in your app, you'll need to determine how you will be
handling the transition from Rooms to your app. Please see Parties on page 56 for more information.
Configure CAL
To configure CAL select your app in the Developer Dashboard and navigate to CAL in the Platform tab.
Select 'Supported' to enable CAL and choose the required minimum and maximum users to allow in the launch
of an app session. The number of users allowed will depend on the type of app you are creating. For example,
Oculus Platform | Coordinated App Launch (CAL) | 19
a game of Chess should require both a minimum and maximum of 2, while an open-ended multiplayer game
could accept groups between 1 and 4.
Enter the minimum version code for the first build that supports the CAL API. For the approval process, the
build can be in any of the testing or development channels.
Once CAL support is enabled and a supported build is uploaded, any account on that release channel will be
able to test the feature in Rooms.
After the configuration is complete and API integrated, you can submit the app for CAL review. Please send
an email requesting review to [email protected]. When the app is approved you will have the option of
setting the Public field to 'Yes' enabling public use of the feature.
User Experience Recommendations
We recommend that your app immediately places users into a social experience. Also, if your app offers
multiple game modes or options, we recommend that you set a default mode so users are taken to a social VR
experience as soon as possible. If session or game customization is available, present these choices after the
users can hear and see each other.
20 | Discoverability | Oculus Platform
Discoverability
Discoverability is a set of features that help users find apps, content, and events targeted to their interests.
You can create Events, In-App Content stories, and Announcements that we'll show users in VR who may be
interested.
Note: The Discoverability features and content described in this guide are only available for Gear VR at
this time.
As the Oculus platform grows with more apps and content, our goal is to help people find apps and
experiences that are relevant to them. The three Discoverability features in Oculus Home show users apps,
updates, and events that they may be interested in. The Discoverability features are:
• Oculus Explore, a stream of targeted content based on the user's interest. Oculus Explore shows photo and
video stories, auto-generated trending stories, and developer created stories like Announcements and InApp Content.
Note: To have a trending app show up in Oculus Explore without creating an Announcement or InApp Content post, you'll need to have submitted a cubemap and a 2D video to the Assets page in
the Oculus store.
• Oculus Events are time specific experiences that show up in both Home and the Oculus 2D app, that users
may subscribe to and receive notifications about. Events are shown to all users, inclusing those who have
not yet downloaded your app.
• Oculus Search, a keyword search that will return results about all apps and in-app content that match the
user's search terms.
Oculus Platform | Discoverability | 21
Create Stories and Events for Users to Discover
You can create stories and events to showcase exciting content for your app in the Oculus Developer Center.
Oculus will then show them to people who may be interested, providing opportunities for people to download
or re-engage with your app.
Announcements
Announce new changes or updates in your app, such as a new game mode, level, social feature, or character
class. Announcements may be shown in Explore if the post matches a user's interests. Announcements will be
shown in the Oculus Explore sections of Home.
Create an Announcement Post
To create an Announcement post, go to your app's Oculus dashboard in the Developer Center and select
Discoverability → Announcements.
Note: These posts are only shown to English speaking users at this time.
Enter the following information to create your announcement or story.
1. Tagline - A 2-5 word sentence about your announcement.
2. Description - A longer form detail about the announcement. The description should be 2-3 sentences
about your story and cannot include HTML or other markup. This should be specific to your announcement
content, not a copy of your app's description.
3. Image - Image for the announcement. This should be different than your cover art. It should reflect your
Announcement, be 16:9 Aspect Ratio, and a 2560 x 1440 24-bit .png file.
4. Facebook Trailer Video URL - After uploading a 2D or 360 video (preferred) to your Facebook Page, copy
and paste the video URL and provide it here. Details about creating the trailer can be found in the 'Create a
Trailer Video' section below. This video is required to create an announcement.
5. Start Time - The time you would like your story to start showing up in Oculus Explore.
6. Deeplink Message (optional) - A string that you create that will be provided as a parameter in the app
launch intent. If a user does not have your app installed when clicking on the deep-link, we will prompt them
to purchase your app.
Integrate Announcements
Integrating deeplink support for Announcements is optional. If you do not use deeplinks, the app will be
launched by the normal launch process.
You'll integrate a hook into your app that listens for a specific launch detail when the app is started,
ovrLaunchType_Deeplink on Native Android and Launchtype.Deeplink on Unity. When you see
these details in the launch event, your app will retrieve the deeplink that you defined to direct the user to the
appropriate location in your app.
For example, on Native Android:
ovrLaunchDetailsHandle handle = ovr_ApplicationLifecycle_GetLaunchDetails();
if (ovr_LaunchDetails_GetLaunchType(handle) == ovrLaunchType_Deeplink) {
string deeplink = ovr_LaunchDetails_GetDeeplinkMessage(handle);
On Unity:
using Oculus.Platform.Models;
22 | Discoverability | Oculus Platform
LaunchDetails launchDetails = new LaunchDetails(CAPI.
ovr_ApplicationLifecycle_GetLaunchDetails());
if (launchDetails.LaunchType == LaunchType.Deeplink) {
String deeplinkMessage = launchDetails.DeeplinkMessage;
...
}
Create a Trailer Video
A 2D video trailer is required to create an Announcement.
You can publish a 2D video to FB as a “Secret” video and use that for your story if you don't want to share the
video outside the Oculus environment.
1. Go to 'Publishing Tools' > 'Video Library'. Information about this process can be found in the Facebook
Publishing FAQ.
2. Select the video.
3. In the 'Video Details' dialog box, click 'Create Post With Video'.
4. Select the Advanced tab.
5. Change 'Distribution' to 'Custom'.
6. Select 'Add as secret video'.
Then, once you've created the video, retrieve the URL.
1.
2.
3.
4.
Go to 'Publishing Tools' > 'Video Library'.
Select the video.
In the 'Video Details' dialog box, select the post you just created (at the bottom).
Click 'View Permalink'.
Content Review
All posts and stories will reviewed by the Oculus team for content approval and compliance with the Oculus
Code of Conduct. If the submission does not comply, it will be rejected.
Events
Use Events to host time-specific gatherings for your users to attend, like a viewing party, social mixer, or game
tournament. Events may be shown in Explore if the post matches a user's interests. Events will be shown in the
Oculus Events section of Home and Oculus Explore if deeplinks are implemented.
Users can subscribe to upcoming VR events and receive a reminder before the event begins. Users can also see
which events friends have subscribed to and join them to create a social experience.
In VR, when a user clicks 'Interested' to subscribe to a future event or 'Join Now' for an event in progress, they
will be prompted to install the app if they have not done so already. Users can also explore and subscribe to
events in the Oculus Android app.
Create an Event
To create an Event , go to your app's Oculus dashboard in the Developer Center and select Discoverability →
Events.
Enter the following information to create your event:
1. Title - The title is the short description of the event that will be shown with the event's image.
Oculus Platform | Discoverability | 23
2. Description - A description of the event. This will be used throughout the platform. The description should
be in plain text.
3. Image - The image that will be displayed in association with the event. The uploaded image should meet
the asset guidelines, and be 2560 x 1440 24bit .png format.
4. Start Time - Start time of the event in your local time zone.
5. End Time - End time of the event in your local time zone.
6. Deeplink Message (optional) - The deeplink message is a message we will include with apps launched
from an event. We'll include the deeplink message with the app launch detail. Information on handling
the deeplink message can be found in the section below. The deeplink message should not exceed 1,500
characters.
2D trailer video are required for events to be shown in Oculus Explore. Trailer videos can be added to the
Assets page in the Oculus store.
Duplicate an Event
You may wish to create a reoccurring event. To duplicate an event, select the options menu next to the event
you wish to duplicate, update the information for the new event, and submit the new event for review.
Integrate Events
Integrating deeplink support into your app is optional. If you do not use deeplinks, the app will be launched
from an event by the normal launch process.
You'll integrate a hook into your app that listens for a specific launch detail when the app is started,
ovrLaunchType_Deeplink on Native Android and Launchtype.Deeplink on Unity. When you see
these details in the launch event, your app will retrieve the deeplink that you defined to direct the user to the
appropriate location in your app.
For example, on Native Android:
ovrLaunchDetailsHandle handle = ovr_ApplicationLifecycle_GetLaunchDetails();
if (ovr_LaunchDetails_GetLaunchType(handle) == ovrLaunchType_Deeplink) {
string deeplink = ovr_LaunchDetails_GetDeeplinkMessage(handle);
On Unity:
using Oculus.Platform.Models;
LaunchDetails launchDetails = new LaunchDetails(CAPI.
ovr_ApplicationLifecycle_GetLaunchDetails());
if (launchDetails.LaunchType == LaunchType.Deeplink) {
String deeplinkMessage = launchDetails.DeeplinkMessage;
...
}
Content Review
All posts and stories will reviewed by the Oculus team for content approval and compliance with the Oculus
Code of Conduct. If the submission does not comply, it will be rejected.
Testing Events
Want to see how your event will look before releasing to the public? To test your event 1.
2.
3.
4.
Create an event and save it as a draft.
Subscribe to the event from your dashboard.
Navigate to 'My Events' to see your event.
Test your deeplink integration.
24 | Discoverability | Oculus Platform
5. Once you're satisfied with the event and functionality, you can submit the event for review and public
release.
In-App Content
Preview self-contained experiences in your app, like a video, photo set, or standalone experience with In-App
Content. In-App Content may be shown in Explore if the post matches a user's interests. In-App Content will
be surfaced in Oculus Search. We can show users your In-App Content post in Oculus Explore if you provide a
trailer video for the post.
Note: These posts are only shown to English speaking users at this time.
Create an In-App Content Post
To create an In-App Content post, go to your app's Oculus dashboard in the Developer Center and select
Discoverability → In-App Content.
Enter the following information to create your announcement or story.
1. Tagline - A 2-5 word sentence about your In-App Content post.
2. Description - A longer form detail about the content. The description should be 2-3 sentences about your
post and cannot include HTML or other markup. This should be specific to your story content, and not just a
copy of your app description.
3. Image - Image for the content. This should be different than your cover art. It should reflect your story and
should be 16:9 Aspect Ratio and a 2560 x 1440 24-bit .png file.
4. Facebook Trailer Video URL (optional but recommended) - After uploading a 2D or 360 video (preferred) to
your Facebook page, copy and paste the video URL and provide it here. Details about creating the trailer
can be found in the 'Create a Trailer Video' section below. This video is optional for In-App Content.
5. Start Time - The time you would like your story to start showing up in Oculus Explore.
6. Deeplink Message - A string that you create that will be provided as a parameter in the app launch intent.
If a user does not have your app installed when clicking on the deep-link, we will prompt them to purchase
your app. Deeplink messages are required for In-App Content posts.
Integrate In-App Content
Integrating deeplink support is required to create an In-App Content post.
You'll integrate a hook into your app that listens for a specific launch detail when the app is started,
ovrLaunchType_Deeplink on Native Android and Launchtype.Deeplink on Unity. When you see
these details in the launch event, your app will retrieve the deeplink that you defined to direct the user to the
appropriate location in your app.
For example, on Native Android:
ovrLaunchDetailsHandle handle = ovr_ApplicationLifecycle_GetLaunchDetails();
if (ovr_LaunchDetails_GetLaunchType(handle) == ovrLaunchType_Deeplink) {
string deeplink = ovr_LaunchDetails_GetDeeplinkMessage(handle);
On Unity:
using Oculus.Platform.Models;
LaunchDetails launchDetails = new LaunchDetails(CAPI.
ovr_ApplicationLifecycle_GetLaunchDetails());
if (launchDetails.LaunchType == LaunchType.Deeplink) {
String deeplinkMessage = launchDetails.DeeplinkMessage;
Oculus Platform | Discoverability | 25
...
}
Create a Trailer Video
A 2D video trailer is recommended for In-App Content. If you do not provide a Facebook 2D video for an InApp Content, we will not be able to show your story or event in Oculus Explore.
You can publish a 2D video to FB as a “Secret” video and use that for your story if you don't want to share the
video outside the Oculus environment.
1. Go to 'Publishing Tools' > 'Video Library'. Information about this process can be found in the Facebook
Publishing FAQ.
2. Select the video.
3. In the 'Video Details' dialog box, click 'Create Post With Video'.
4. Select the Advanced tab.
5. Change 'Distribution' to 'Custom'.
6. Select 'Add as secret video'.
Then, once you've created the video, retrieve the URL.
1.
2.
3.
4.
Go to 'Publishing Tools' > 'Video Library'.
Select the video.
In the 'Video Details' dialog box, select the post you just created (at the bottom).
Click 'View Permalink'.
Content Review
All posts and stories will reviewed by the Oculus team for content approval and compliance with the Oculus
Code of Conduct. If the submission does not comply, it will be rejected.
26 | Commerce (IAP) | Oculus Platform
Commerce (IAP)
In-app purchases (IAP) allow users to purchase items without leaving your app.
There are two types of items that you can offer in your game, consumable and durable.
• Durable - Durable items are single-use items that persist with the user. Meaning, once the item has been
purchased, it cannot be purchased again. For example, you may offer the initial game download for free
with only a demo level, then offer additional levels as in-app purchases.
• Consumable - Consumable items can be purchased multiple times. For example, consumable IAP may by
'character lives' or 'coins' that are used during the course of gameplay. Consumable purchases must be
ingested by your app before they can be made available for purchased again.
Note: All IAP operations require the user to be connected to the internet.
Defining Items for Purchase
Before you begin integrating the IAP methods in your experience, you’ll need to define the items that you will
be offering for purchase. The in-app purchase list can be updated or modified at any time.
At the moment there is no support for selling a single item with multiple currencies. A separate SKU will need
to be created for each item and currency combination.
To define your IAP list open your preferred spreadsheet or text editor and create a tab-separated (.tsv) file in
the following format.
SKU
NAME
unlock_level_2Level 2 Unlock
100_coins
100 Coins
Description
Purchase this
item to unlock
the 2nd level in
the game.
100 coins to
spend as ingame currency.
Currency
Amount
Item Type
USD
9.99
durable
USD
2.99
consumable
The values for the items in the table above are:
• SKU - The unique string that you use to reference the IAP item in your app.
• Name - The short descriptive name that the user will see.
• Description - The full description of the item the user will see. Be as descriptive as necessary to avoid any
confusion.
• Currency - Currency is the unit of currency that the item will be charged in. Supported currencies are: USD,
CAD, EUR, GBP, JPY, KRW, and AUD.
• Amount - The decimal amount to charge for the IAP item.
• Item Type - The type of item for sale. Available options are 'consumable' and 'durable'. Please see the
description above for the difference between the two.
An IAP item template is available for download from the IAP section of the Developer Center.
Once you’ve created your file, you can upload your IAP items to your selected experience in the IAP section of
the Developer Center and selecting ‘Upload TSV’ and following the on-screen prompts.
Oculus Platform | Commerce (IAP) | 27
Note: You must enter your payment information before you can download the template or upload a
file.
Integrating IAP
Once you’ve finished defining the items that you would like to offer as purchases, you can start building them
as purchasable items into your app.
The following SDK methods can be called from your client app.
• Retrieve the user's purchased items:
Native - ovr_IAP_GetViewerPurchases()
Unity - Platform.IAP.GetViewerPurchases()
Review the ovr_IAP_GetViewerPurchases page for information about the parameters and return data
structure. A secure S2S API that preforms the same function is described in the S2S REST API section below.
• Retrieve a list of available items and prices by SKU:
Native - ovr_IAP_GetProductsBySKU()
Unity - Platform.IAP.GetProductsBySKU()
Review the ovr_IAP_GetProductsBySKU page for information about the parameters and return data
structure.
• Launch the checkout flow for a SKU (Purchase an item):
Native - ovr_IAP_LaunchCheckoutFlow()
Unity - Platform.IAP.LaunchCheckoutFlow()
Review the ovr_IAP_LaunchCheckoutFlow page for information about the parameters and return data
structure.
• Consume a purchased item:
Native - ovr_IAP_ConsumePurchase()
Unity - Platform.IAP.ConsumePurchase()
Review the ovr_IAP_ConsumePurchase page for information about the parameters and return data structure.
A secure S2S API that preforms the same function is described in the S2S REST API section below.
Example Implementation
The following Unity example demonstrates the end-to-end flow of retrieving information for an IAP item,
displaying that information to the user, consuming any outstanding purchases, and initiating the checkout
flow when a user indicates that they would like to make a purchase. The following example is taken from the
VRBoardGame sample app. Please see the Sample Apps page for more information about the apps that are
available.
using
using
using
using
UnityEngine;
Oculus.Platform;
Oculus.Platform.Models;
UnityEngine.UI;
// This class coordinates In-App-Purchases (IAP) for the application. Follow the
// instructions in the Readme for setting up IAP on the Oculus Dashboard. Only
// one consumable IAP item is used is the demo: the Power-Ball!
public class IAPManager : MonoBehaviour
{
// the game controller to notify when the user purchase more powerballs
28 | Commerce (IAP) | Oculus Platform
[SerializeField] private GameController m_gameController;
// where to record to display the current price for the IAP item
[SerializeField] private Text m_priceText;
// purchasable IAP products we've configured on the Oculus Dashboard
private const string CONSUMABLE_1 = "PowerballPack1";
void Start()
{
FetchProductPrices();
FetchPurchasedProducts();
}
// get the current price for the configured IAP item
public void FetchProductPrices()
{
string[] skus = { CONSUMABLE_1 };
IAP.GetProductsBySKU(skus).OnComplete(GetProductsBySKUCallback);
}
void GetProductsBySKUCallback(Message<ProductList> msg)
{
if (msg.IsError)
{
PlatformManager.TerminateWithError(msg);
return;
}
}
foreach (Product p in msg.GetProductList())
{
Debug.LogFormat("Product: sku:{0} name:{1} price:{2}", p.Sku, p.Name, p.FormattedPrice);
if (p.Sku == CONSUMABLE_1)
{
m_priceText.text = p.FormattedPrice;
}
}
// fetches the Durable purchased IAP items. should return none unless you are expanding the
// to sample to include them.
public void FetchPurchasedProducts()
{
IAP.GetViewerPurchases().OnComplete(GetViewerPurchasesCallback);
}
void GetViewerPurchasesCallback(Message<PurchaseList> msg)
{
if (msg.IsError)
{
PlatformManager.TerminateWithError(msg);
return;
}
}
foreach (Purchase p in msg.GetPurchaseList())
{
Debug.LogFormat("Purchased: sku:{0} granttime:{1} id:{2}", p.Sku, p.GrantTime, p.ID);
}
public void BuyPowerBallsPressed()
{
#if UNITY_EDITOR
m_gameController.AddPowerballs(1);
#else
IAP.LaunchCheckoutFlow(CONSUMABLE_1).OnComplete(LaunchCheckoutFlowCallback);
#endif
}
private void LaunchCheckoutFlowCallback(Message<Purchase> msg)
{
if (msg.IsError)
{
PlatformManager.TerminateWithError(msg);
return;
}
Purchase p = msg.GetPurchase();
Debug.Log("purchased " + p.Sku);
m_gameController.AddPowerballs(3);
Oculus Platform | Commerce (IAP) | 29
}
}
S2S REST Requests
Our S2S REST APIs are available as a secure channel to interact with the Oculus Platform. For example, you may
wish to track and consume coins purchased through in-app purchases on your trusted server. This prevents any
client-side tampering to grant unpurchased gems. Using the S2S APIs are not required, but may be used if you
wish.
See the Server-to-Server API Basics page for information about interacting with our APIs.
Parameter
Required
Description
Type
Example
sku
Y
The sku for the
item, defined when
created on the
Developer Center.
string
"50_gems"
Verify Item Ownership
Verify that a user owns an item.
Example Request
$ curl -d "access_token=$USER_ACCESSTOKEN" -d "user_id=$USERID"
https://graph.oculus.com/$APPID/verify_entitlement
Example Response
{"success":true}
Consume an IAP Item
Consume an IAP item that a user has purchased.
Example Request
$ curl -d "access_token=$USER_ACCESSTOKEN" -d "sku=$SKU"
https://graph.oculus.com/$APPID/consume_entitlement
Example Response
{"success":true}
Retrieve Items Owned
Retrieve a list of items that the user owns.
Example Request
$ curl -G -d "access_token=$USER_ACCESSTOKEN" -d "sku=$SKU" -d 'fields'='item{skus},id'
https://graph.oculus.com/$APPID/viewer_purchases
Example Response
{
"data": [
{
"id": "963119010431337",
30 | Commerce (IAP) | Oculus Platform
}
]
}
"item": {
"sku": "EXAMPLE1"
}
Test IAP
You can test your IAP integration by creating test users for your organization. These test users have permission
to purchase IAP items in your app, including the Alpha, Beta, and Release Candidate apps, without using actual
money. These users will work with all applications in your organization and can be deleted at any time.
Create a test user by navigating to your Org's publishing page on the Developer Center and selecting the 'Test
Users' tab. On this page select ‘+ Add Test User’ and enter a password and pin on the modal that opens. Click
‘Submit’ to create the user. This user will be assigned the name of the person who’s account created the test
user, a random Oculus Id, and an email address based on the account creators email address. This user inherits
permissions to all apps and channels for the Org that you created the user in.
Once the test user has been created, log in to that account and add a payment method for the user. Since this
user will be testing the IAP checkout flow, add the following credit card numbers to test different IAP flows.
• Always succeeds - 4111 1177 1155 2927
• Fails at authorization - 4111 1180 9636 6644
• Fails at sale - 4111 1193 1540 5122
These cards only work with test users and can only be used within your organization. When using the test credit
cards you must use a valid address and an expiration date that has not already passed.
Oculus Platform | Leaderboards | 31
Leaderboards
Include leaderboards in your game to create competition and increase engagement among your users.
The Oculus Platform will manage your leaderboard data. However, your app will be responsible for displaying,
reporting, and verifying the data.
This page will walk you through how to create your global leaderboards, interact with the leaderboards service,
and provide an example Unity implementation you can review or use as a leaderboard template.
Create Your Leaderboards
The first step in adding leaderboards to your game is defining them in the Developer Center. To get started,
navigate to Leaderboards in the Developer Center and select the app that you would like to create a
leaderboard for.
Select ‘Create Leaderboard’ and enter the following information:
• API Name - This is the unique string that you will allow you to reference this leaderboard in your app.
• Sort Order - There are two options for Sort Order depending on your use-case:
• ‘Higher is Better’ will rank users in descending order, from highest to lowest score.
• ‘Lower is Better’ will rank users in ascending order, from lowest to highest score.
Select ‘Submit’ when finished to save the leaderboard. You can update leaderboard settings at any time in the
Developer Center. You may also clear the results in a leaderboard and reset the scores.
Integrating Leaderboards
Once you’re finished creating the achievements, you can begin to integrate them in your game. When calling
the SDK methods in this section use the ‘API Name’ you defined in the Developer Center.
• Retrieve a list of leaderboard entries:
Native - ovr_Leaderboard_GetEntries()
Unity - Platform.Leaderboard.GetEntries()
Review the ovr_Leaderboard_GetEntries page for information about the parameters and return data
structure.
• Retrieve a list of leaderboard entries after a rank:
Native - ovr_Leaderboard_GetEntriesAfterRank()
Unity - Platform.Leaderboard.GetEntriesAfterRank()
Retrieve leaderboard entries after rank allows you to retrieve a list of entries starting after a rank that you
define. For example, if you specify a list with an 'afterRank' of 10, you’ll get a list starting with the 11th
position. Review the ovr_Leaderboard_GetEntriesAfterRank page for information about the parameters and
return data structure.
• Retrieve the next list of entries:
Native - ovr_Leaderboard_GetNextEntries()
Unity - Platform.Leaderboard.GetNextEntries()
Review the ovr_Leaderboard_GetNextEntries page for information about the parameters and return data
structure.
32 | Leaderboards | Oculus Platform
• Retrieve the previous list of entries:
Native - ovr_Leaderboard_GetPreviousEntries()
Unity - Platform.Leaderboard.GetPreviousEntries()
Review the ovr_Leaderboard_GetPreviousEntries page for information about the parameters and return data
structure.
• Write leaderboard entry:
Native - ovr_Leaderboard_WriteEntry()
Unity - Platform.Leaderboard.WriteEntry()
This method will write a new leaderboard entry, to the specified leaderboard, for the current user. It is not
an incremental update, it will overwrite the existing entry, so be sure to write the complete value. Review the
ovr_Leaderboard_WriteEntry page for information about the parameters and return data structure.
Implementation
At the highest level, there are two processes to implement when integrating leaderboards.
1. Retrieve and Display Leaderboards - Display the current leaderboard state before a game begins. For
example, ovr_Leaderboard_GetEntries for a native app, or Platform.Leaderboards.GetEntries
for Unity. There are other methods you can use to get a subset of leaderboard entries based on input
criteria.
2. Update Leaderboard Entries - Write the results of the current game to your leaderboard. For example,
ovr_Leaderboard_WriteEntry for a native app, or Platform.Leaderboards.WriteEntry for a
Unity app. A user may only have one entry on each leaderboard, subsequent entries will overwrite the
existing entry on the specified leaderboard.
Example Implementation
The following Unity example demonstrates retrieving information from a leaderboard called
'MOST_MATCHES_WON' and writing a new leaderboard entry after a win for the current user. The following
example is taken from the VRHoops sample app, please download the full app for the full example with other
features. and see the Sample Apps page for more information about the apps that are available.
using
using
using
using
UnityEngine;
System.Collections.Generic;
Oculus.Platform;
Oculus.Platform.Models;
// Coordinates updating leaderboard scores and polling for leaderboard updates.
public class LeaderboardManager
{
// API NAME for the leaderboard where we store how many matches the user has won
private const string MOST_MATCHES_WON = "MOST_MATCHES_WON";
///...
// the top number of entries to query
private const int TOP_N_COUNT = 5;
// how often to poll the service for leaderboard updates
private const float LEADERBOARD_POLL_FREQ = 30.0f;
// the next time to check for leaderboard updates
private float m_nextCheckTime;
///...
// whether we've found the local user's entry yet
private bool m_foundLocalUserMostWinsEntry;
// number of times the local user has won
private long m_numWins;
Oculus Platform | Leaderboards | 33
// callback to deliver the most-wins leaderboard entries
private OnMostWinsLeaderboardUpdated m_mostWinsCallback;
///...
public void CheckForUpdates()
{
if (Time.time >= m_nextCheckTime &&
PlatformManager.CurrentState == PlatformManager.State.WAITING_TO_PRACTICE_OR_MATCHMAKE)
{
m_nextCheckTime = Time.time + LEADERBOARD_POLL_FREQ;
}
}
QueryMostWinsLeaderboard();
QueryHighScoreLeaderboard();
#region Most Wins Leaderboard
public delegate void OnMostWinsLeaderboardUpdated(SortedDictionary<int, LeaderboardEntry>
entries);
public OnMostWinsLeaderboardUpdated MostWinsLeaderboardUpdatedCallback
{
set { m_mostWinsCallback = value; }
}
void QueryMostWinsLeaderboard()
{
// if a query is already in progress, don't start a new one.
if (m_mostWins != null)
return;
m_mostWins = new SortedDictionary<int, LeaderboardEntry>();
m_foundLocalUserMostWinsEntry = false;
}
Leaderboards.GetEntries(MOST_MATCHES_WON, TOP_N_COUNT, LeaderboardFilterType.None,
LeaderboardStartAt.Top).OnComplete(MostWinsGetEntriesCallback);
void MostWinsGetEntriesCallback(Message<LeaderboardEntryList> msg)
{
if (!msg.IsError)
{
foreach (LeaderboardEntry entry in msg.Data)
{
m_mostWins[entry.Rank] = entry;
}
if (entry.User.ID == PlatformManager.MyID)
{
m_foundLocalUserMostWinsEntry = true;
m_numWins = entry.Score;
}
// results might be paged for large requests
if (msg.Data.HasNextPage)
{
Leaderboards.GetNextEntries(msg.Data).OnComplete(MostWinsGetEntriesCallback);
return;
}
// if local user not in the top, get their position specifically
if (!m_foundLocalUserMostWinsEntry)
{
Leaderboards.GetEntries(MOST_MATCHES_WON, 1, LeaderboardFilterType.None,
LeaderboardStartAt.CenteredOnViewer).OnComplete(MostWinsGetEntriesCallback);
return;
}
}
// else an error is returned if the local player isn't ranked - we can ignore that
}
if (m_mostWinsCallback != null)
{
m_mostWinsCallback(m_mostWins);
}
m_mostWins = null;
34 | Leaderboards | Oculus Platform
#endregion
// submit the local player's match score to the leaderboard service
public void SubmitMatchScores(bool wonMatch, uint score)
{
if (wonMatch)
{
m_numWins += 1;
Leaderboards.WriteEntry(MOST_MATCHES_WON, m_numWins);
}
}
}
if (score > 0)
{
Leaderboards.WriteEntry(HIGHEST_MATCH_SCORE, score);
}
Implementation Tips
When implementing leaderboards, there are two common scenarios you should be aware of. They are:
• To retrieve leaderboard entries centered around the current user, use the LeaderboardStartAt enum to
define where the values returned start or are centered. To retrieve only the current user, center on the
viewer and limit results returned to 1.
• To return only the user's friends, you can use the LeaderboardFilterType enum to define the results
returned.
S2S REST Requests
Certain actions require to your back-end servers to interact directly with our platform. For example, update a
leaderboard after a server-hosted multiplayer match. See the Server-to-Server API Basics page for information
about interacting with our APIs.
Leaderboards S2S Parameters
Parameter
Required
Description
api_name
Y
The unique string
string
that you will allow
you to reference this
leaderboard in your
app.
Y for 'Create and
Update Leaderbord
Entry'
The leaderboard
value or score.
integer
78645
Extra metadata
to store with the
leaderboard entry.
This can be used to
specify information
about the score.
For example, in a
driving game this
may be what car
was used. Decoded
length can be, at
most, 2048 bytes.
string
“T2N1bHVz”
score
extra_data_base64N
Type
Example
“top_players1”
Oculus Platform | Leaderboards | 35
Parameter
force_update
user_id
Required
Description
Type
N - default is false If you already have
boolean
an entry on the
leaderboard and
the user recieves a
worse score than the
existing leaderboard
entry, the existing
entry will not be
updated unless
force_update is
true. Set as true
to overwrite the
entry even if the
new score being
posted is worse than
the old one.
Only if you use the
App Access Token
to authenticate to
the API.
Indicate which user string
you are posting
on behalf of. That
user must have an
entitlement to your
app. When using a
User Access Token,
this field must not
be set. See the User
and Friends on page
66 page for
information about
retrieving the User
Id.
Example
“false”
“12345”
Create and Update a Leaderboard Entry (POST)
Example Request
$ curl -d "access_token=$APP_ACCESSTOKEN|$USER_ACCESSTOKEN" -d "api_name=MY_NEW_LEADERBOARD"
-d "score=12345" -d "extra_data_base64=T2N1bHVz" -d "force_update=true" -d "user_id=865302060207175"
https://graph.oculus.com/leaderboard_submit_entry
Example Response
{
}
"success": true,
"did_update": true
The response contains a status. did_update indicates whether the entry was recorded or not.
Entries will not be recorded if the user already has an entry on the leaderboard, the new score is worse than the
old score, and force_update is false.
Delete a Leaderboard Entry (DELETE)
36 | Leaderboards | Oculus Platform
Example Request
curl -X "DELETE" -d "access_token=$APP_ACCESSTOKEN"
https://graph.oculus.com/$leaderboard-entry-id
Example Response
{
}
"Success":true
Once deleted, a leaderboard entry cannot be recovered.
Oculus Platform | Matchmaking | 37
Matchmaking
Matchmaking places users together in a shared multiplayer experience. User matching can be done by
common skill or other criteria that you define. The Matchmaking service offers two modes, Quickmatch and
Browse.
Matchmaking works in combination with another Oculus Platform feature, Rooms on page 58, to provide
a full multiplayer experience in VR. Matchmaking places users together in a room for a gameplay session,
and the room the hosts and manages the gameplay session. Please see the Rooms on page 58 page for
information about the different types of rooms available. For the purposes of integrating Matchmaking, you
should be aware that there are two different types of matchmaking rooms that are used by the service, usercreated matchmaking rooms and system-generated matchmaking rooms. As the names suggests, user-created
rooms are created and owned by users, where system-generated rooms are created and owned by your app.
Matchmaking is frequently used in combination with Leaderboards on page 31 to rank and compare
multiplayer users creating a competition among players.
Matchmaking Modes
There are two supported matchmaking modes, Quickmatch and Browse. Choose the multiplayer experience
that you want your players to have.
• Quickmatch - Quickmatch allows players to join a matchmaking queue, and be automatically matched into a
room for a multiplayer session. There are two types of Quickmatch:
• Simple Quickmatch - In Simple Quickmatch, rooms are created for players once a match has been made.
Simple Quickmatch is designed for 2 player games, like chess or checkers, where users don't need the
ability to join matches in progress.
• Advanced Quickmatch - Advanced Quickmatch can be configured where rooms are created by the user,
by the Matchmaking service, or both. This allows the matchmaking service to host two matchmaking
queues, one for rooms supporting multiple game settings and one for users looking for rooms. Use this
mode for more complicated games with multiple match options or settings, and where users may join, or
leave, during the course of a match.
• Browse - In Browse, users can create and host rooms or choose from a list of rooms to join. Browse also
supports more complicated games with multiple match options or settings, and where users may want to
join, and leave, a match in progress.
Using this Guide
This guide will walk you through the matchmaking configurations and SDK basics, a basic matchmaking
implementation, how to add more advanced user matching, and finally how to test and tune your
implementation. We recommend reading through the whole Matchmaking guide before you begin your
integration.
Configuration Overview
This page will walk you through the concepts of matchmaking, and the configuration details available in the
Developer Center. The Matchmaking Quickstart and Additional Configurations pages will walk you through
how to implement Matchmaking in your game.
38 | Matchmaking | Oculus Platform
Matchmaking Configuration Options
The Oculus Developer Center allows you to define how you want to groups your players and matchmaking
configuration that determine how you'll bring them together. There are three Matchmaking concepts that we’ll
review in this guide: Pools, Data Settings, and Queries.
Pools
Pools are the top level groups of users in matchmaking. This is where you define the Matchmaking mode that
you want to use, the baseline networking requirements necessary to match users, and the high-level criteria
required to make a match.
There are two types of pools you can create, matchmaking pools and skill pool. Pools are the basic
matchmaking configurations for a group of users, game-type, or game mode. The Pools define the number
of users per matchmaking session, the connectivity requirements, and other high-level matchmaking tuning.
Skill pools can be created and added to matchmaking pools to account for a user’s cumulative skill level when
making matches.
If you create multiple pools and skill pools for the same app, each pool created will have a separate queue of
users. Users in different pools cannot be matched.
Create a Matchmaking Pool
To create a matchmaking pool, navigate to Matchmaking in the Developer Center, use the drop-down to select
'Pool', click ‘Create Pool’, and enter your pool details.
• Pool Key - This is the unique string that you will allow you to reference this matchmaking pool in your app.
• Mode - Select one of the matchmaking modes (Quickmatch or Browse) described above.
• Users per Match - Users per match is four configurations that detail how many players are supported in a
session.
• Min Users - The minimum number of users required to initiate a session.
• Min Preferred Users - (Optional) The ideal minimum number of users in a session. Fewer users is
supported (down to the Min Users defined), but the game experience may be degraded.
• Max Preferred Users - (Optional) The ideal maximum number of users in a session. More users is
supported (up to the Max Users defined), but the game experience may be degraded.
• Max Users - The maximum number of users supported in a session.
• Skill Pool - If you would like to add a skill dimension to your matchmaking pool, please see the section
below about creating a skill pool. Once the skill pool has been created you can add it to the multiplayer
pool.
• Advanced Quickmatch - Select whether you would like this pool to support Advanced Quickmatch.
Information about Advanced Quickmatch can be found on the Matchmaking page.
• Can people create and enqueue rooms themselves? - Select whether you would like to allow users to
create their own matchmaking rooms. If disabled, only system generated rooms will be supported.
• Can the system create rooms to match people into? - Select whether you would like to allow the
matchmaking service to create rooms when it finds matches. If disabled, the service will rely on users to
create and enqueue rooms.
• Can unmatched people join matchmaking rooms? - Select whether to allow users not matched by the
matchmaking service to join a room, i.e. members of a party or friends of the matched user. If disabled,
only users matched by the service can be added to a matchmaking room.
• Can the matchmaking service keep matching into the same room? - Select whether to allow users to be
matched to the same matchmaking room multiple times. For example, enabling would allow users to join
an in-progress game or trickle into a room one by one. If disabled, once a match is made, the room will
be removed from the queue.
Oculus Platform | Matchmaking | 39
• Enable host migration when the room owner leaves the room? - Select whether you would like to transfer
ownership of a user-created room in the event that the room owner leaves. The person who has been in
the room the longest will become the room owner automatically if enabled.
• Should Consider Ping Time? - Choose if you would like the matchmaking service to consider peer-to-peer
latency when matching users. If yes:
• Acceptable Ping Time (ms) - The maximum acceptable ping time for users to be matched.
• Good Ping Time (ms) -The longest ping time before multiplayer experience will be affected.
• Advanced Tuning - Advanced tuning options are available if you’d like to further refine the pools of users.
Select ‘Show Advanced Tuning…’ to reveal these options. We'll review how to use these tuning options in
the Testing and Tuning on page 48 page.
• Minimum Quality Bar - (Optional) A ratio defining the minimum Match Quality where a match should
be made. We recommend starting with the default 0.5 and using this value to fine-tune matches. Small
multiplayer populations may wish to reduce this number to allow users to be matched more easily.
See the next section for information about how we determine match quality. The multiplayer queries
described below will also affect the Match Quality.
• Reservation Period - (Optional) This is the amount of time, in seconds, that a user has to accept a
multiplayer session before their spot is released to another user.
• Suggested Rampdown - (Optional) Potential matches are ranked in terms of their Match Quality. When
users enter the matchmaking queue the match quality the service will try to match the user with is 1, the
required score gradually decreases adding incrementally less ideal matches over time. The Suggested
Rampdown is the time, in seconds, for the score to decrease from 1 to your defined Minimum Quality
Bar.
Create a Skill Pool
To create a skill pool, use the drop-down to select skill pool, click ‘Create Pool’, and enter the following
information for your skill pool.
• Skill Pool Key - This is the unique string that you will use to reference this skill pool.
• Luck Factor - Luck Factor is a qualitative judgment about how much luck is involved in your game. Card
games would have a higher Luck Factor and it would be appropriate to allow players of a wide skill range,
while a skill-based game, like chess, rely more on skill and less on luck.
• Draw Probability - This is a number (between 0 and 1, inclusive) that is your estimate that a match of evenlymatched players will result in a draw.
• Skill Reset - Skill reset allows you to reduce, or normalize, the skill ratings for all players by a certain amount
at a specified time.
Data Settings
Data Settings are the key/value pair data that the enqueuing player or room provides about itself at enqueue
time. Data Settings can be used both to determine what a player is looking for in a match, as well as what a
player looks like to another player. For example, if a player may be enqueued with the type of match they want
to play, the map they want to play on, and the level they have achieved in the game. You may also use Data
Settings to ensure that matches are only made with users who are using the same version of your app.
To add Data Settings to a matchmaking pool, navigate to Matchmaking in the Developer Center, click the more
options selection for the pool, and then click ‘Manage Queries’. Then, in the Matchmaking Queries page, click
‘Edit Data Settings’.
• Key - This is the unique string that you will use to reference this Data Setting.
• Type - The type of data you are entering. Options are:
• DOUBLE - A decimal value to 2 decimal places.
• INTEGER - An integer.
40 | Matchmaking | Oculus Platform
• INTEGER_BITSET - A hex bitset.
• STRING - An enumerated string where you define the acceptable values.
• Default Value - The default value of the Data Setting if a value is not provided.
We'll review how to implement Data Settings by applying them to users or rooms at enqueue time on the
Adding Skill and Using Queries on page 45 page.
Matchmaking Queries
Matchmaking Queries allow you to define the criteria that determines whether enqueued players and rooms
can be matched with each other. These expressions compare the Data Settings of potential matches against
each other. You define the Matchmaking Queries in the Developer Dashboard. At enqueue time, the Data
Settings provided for the user or room will be compared with potential matches using the Matchmaking
Queries.
A Matchmaking Query is composed of one or more expressions that make up a conditional statement. The
Matchmaking service populates each expression with the Data Settings of the user and potential match
candidate, and determines the quality of the potential match. The 'How do we determine who gets matched?'
section below will review how we use this information to compare potential matches.
To add a Matchmaking Query Expression to a Matchmaking Pool, navigate to Matchmaking in the Developer
Center, click the more options selection for the Pool, and click ‘Manage Queries’. Then, in the Matchmaking
Queries page, click ‘Create Query’.
• Query Key - This is the unique string that you will use to reference this Matchmaking Query.
• Importance - You will configure an importance for the expression. When an expression passes, it evaluates
to a value of 1, and otherwise (failure case) evaluates to the value of the associated importance. Note that
the match-on-failure delay times below are calculated based on a rampdown time of 30 seconds. The
greater the assigned importance, the less likely a match will occur if the expression fails. And in the case of
expressions with 'Required' importance, a failure will never result in a match.
• Required: 0, i.e. never matches on failure.
• High: ~0.55, i.e. matches on failure after 27 seconds.
• Medium: ~0.75, i.e. matches on failure after 15 seconds.
• Low: ~0.9, i.e. matches on failure after 6 seconds.
• Expression - The Expression is where you define what criteria must be, or you’d prefer to be, met in order
for a match to be made. You can define that a Data Setting must be in a specified range of a defined value
or the other users Data Setting.
How do we determine who gets matched?
Each potential match between users is assigned a Match Quality value between 0 and 1. When determining the
quality of a match between users, the criteria values of the criteria considered, like Ping Time, Skill, and Queries
get multiplied together to get a single Match Quality value. A successful match is made when the Match
Quality exceeds the match threshold. The match threshold decreases over time that a user is enqueued. The
Match Quality value and how quickly the match threshold decreases can be configured in your matchmaking
pool. We recommend leaving these values as the default until you have data to evaluate the quality of your
matches. We'll review how to tune your matches in the Testing and Tuning on page 48 section.
A value of 0.5 is considered to be a marginal match, while 0.9 an excellent match. A successful match occurs
if the match value is greater than or equal to the match threshold. With the rampdown, a match threshold is
1.0 at enqueue time seeking a perfect match, but will decrease to 0.5 over a rampdown period of 30 seconds
(default, the rampdown time can be configured in your Pools) where less ideal matches will be accepted.
Note: Match calculations are asymmetric, meaning that if we are determining whether users A and B
can match each other, the match calculation must succeed in the both the A -> B direction and the B ->
A direction in order for them to be matched.
Oculus Platform | Matchmaking | 41
SDK Overview
This page will walk you through the Matchmaking SDK concepts. The Matchmaking Quickstart and Additional
Configurations pages will walk you through how to implement these methods in your game.
The following SDK methods are available to call from your client app. With Matchmaking SDK methods listed
below, we recommend waiting for response messages before making additional requests. Making a call before
the previous call has been handled creates a race condition that may result in unintended actions.
We’ll review the main SDK methods in OVR_Requests_Matchmaking.h. You should review the other
Matchmaking header files, as well as the Reference section, to see all available requests.
• Enqueue the user:
Native - ovr_Matchmaking_Enqueue2()
Unity - Platform.Matchmaking.Enqueue2()
ovr_Matchmaking_Enqueue2 adds the user to the Quickmatch queue, the user will be continually reenqueued until they are successfully matched or cancel the process. Match notifications will come in the
form of an ovrMessage_Notification_Matchmaking_MatchFound notification with the details of
the match. Your app should be listening for notifications on the message queue, please see Requests and
Messages for information about the message queue.
Review the ovr_Matchmaking_Enqueue2 page for information about the parameters and return data
structure.
• Create and enqueue a Room:
Native - ovr_Matchmaking_CreateAndEnqueueRoom2()
Unity - Platform.Matchmaking.CreateAndEnqueueRoom2()
ovr_Matchmaking_CreateAndEnqueueRoom2 creates and enqueues a user-created, multiplayercapable, Room. Review the ovr_Matchmaking_CreateAndEnqueueRoom2 page for information about
the parameters and return data structure.
Review the ovr_Matchmaking_CreateAndEnqueueRoom2 page for information about the parameters and
return data structure.
• Create a Matchmaking Room:
Native - ovr_Matchmaking_CreateRoom2()
Unity - Platform.Matchmaking.CreateRoom2()
ovr_Matchmaking_CreateRoom2 creates a user-created, multiplayer-capable, Room. Review the
ovr_Matchmaking_CreateRoom2 page for information about the parameters and return data structure.
• Enqueue a Matchmaking Room:
Native - ovr_Matchmaking_EnqueueRoom2()
Unity - Platform.Matchmaking.EnqueueRoom2()
ovr_Matchmaking_EnqueueRoom2 adds User Generated rooms previously created
by ovr_Matchmaking_CreateRoom2 to the matchmaking queue. Review the
ovr_Matchmaking_EnqueueRoom2 page for information about the parameters and return data structure.
Note: Private user-created rooms created by ovr_Room_CreateAndJoinPrivate() cannot be
upgraded to a matchmaking room or enqueued in the matchmaking service.
• Browse the list of available Rooms to join:
42 | Matchmaking | Oculus Platform
Native - ovr_Matchmaking_Browse2()
Unity - Platform.Matchmaking.Browse2()
ovr_Matchmaking_Browse2 will return a list of Rooms that the user can join. This call will not return all
Rooms, only the Rooms where the user meets the join criteria. Review the ovr_Matchmaking_Browse2 page
for information about the parameters and return data structure.
• Cancel the Matchmaking request:
Native - ovr_Matchmaking_Cancel2()
Unity - Platform.Matchmaking.Cancel2()
Cancels the matchmaking request. This can be called at any time in the Matchmaking process. If the user
has already joined the Room you will need to call ovr_Room_Leave to remove the user. Review the
ovr_Matchmaking_Cancel2 page for information about the parameters and return data structure.
• Report results of a match (Matchmaking with skill only):
Native - ovr_Matchmaking_ReportResultInsecure()
Unity - Platform.Matchmaking.ReportResultInsecure()
After the match is over, you’ll want to report the results of the match for consideration in future matches.
Review the ovr_Matchmaking_ReportResultInsecure page for information about the parameters and return
data structure.
• Start a match (Matchmaking with skill only):
Native - ovr_Matchmaking_StartMatch()
Unity - Platform.Matchmaking.StartMatch()
After the match is over, you’ll want to report the results of the match for consideration in future matches.
Review the ovr_Matchmaking_StartMatch page for information about the parameters and return data
structure.
Matchmaking Quickstart w/Advanced Options
Now that you’ve familiarized yourself with the Platform and SDK configurations, we can start to implement
Matchmaking in your game. We recommend integrating the Matchmaking service in steps, starting with the
basics and then introducing the additional configurations like Data Settings, Queries, and Skill. We'll review
how to add those on the Adding Skill and Using Queries on page 45 page.
The first step is to determine what type of game you’re making. We introduced the different modes in
Matchmaking on page 37, and different game types call for different Matchmaking modes.
We’ll review a basic implementation for Simple Quickmatch, Advanced Quickmatch, and Browse. Details about
Pool configurations were introduced on the Configuration Overview on page 37 page.
Simple Quickmatch
Let's start with the most basic Matchmaking scenario and make a 2 player game where we won't match based
on skill. For example, a board game like checkers or tic-tac-toe, would be a candidate for Simple Quickmatch.
Simple Quickmatch will match users into system-generated rooms.
1. Create your matchmaking pool, in the Matchmaking section of the Developer Center, with the following
configurations:
Oculus Platform | Matchmaking | 43
• Pool Key = ‘TicTacToe_BestOfThree’ or any value you'd like to use in your game.
2.
3.
4.
5.
6.
• Mode = ‘Quickmatch’
• Users per Match = ‘2’ for all fields.
• Skill Pool = ‘None’
• Advanced Quickmatch = ‘No’
• Should Consider Ping Time? = ‘No’
• When you're finished entering the pool configurations, select 'Save and Deploy' to save the pool.
After you've created your pool, you can begin integrating into your client-side app. To add the user to the
matchmaking queue, call ovr_Matchmaking_Enqueue2() from the client app. The user will be continually
re-enqueued until they are successfully matched or cancel the process. After enqueueing the user, listen for
the match notification ovrMessage_Notification_Matchmaking_MatchFound. Information about how
the Platform SDK uses notifications can be found on the Requests and Messages page.
(Optional) Review the methods in the OVR_MatchmakingEnqueueResult.h file for a list of methods you
can call for information about the health of the queue that you may display to the user at this time.
In your notification handler, call ovr_Room_Join2 with the Room Id from the notification to place the user
into the room.
Your game will be listening for ovrNotification_Room_RoomUpdate for the number of users in the
room. When the desired number of users is reached, your app will launch the game.
At any time in the matchmaking process, the user can call ovr_Matchmaking_Cancel2() to remove
themselves from the queue and exit the matchmaking process.
Example Implementation
Our Unity sample app, VRHoops, is a simple ball shooting game that uses Simple Quickmatch. Please see the
Sample Apps page for more information about the available sample apps.
Advanced Quickmatch
If we're building a multiplayer game that involves more players (2-8) and has multiple modes, using Advanced
Quickmatch will allows both users and the Matchmaking service to create rooms and matches for multiple
users. For example, a first-person shooter may be a candidate for Advanced Quickmatch as there may be
multiple maps or users may wish to join matches already in progress.
1. Create your matchmaking pool, in the Matchmaking section of the Developer Center, with the following
configurations:
•
•
•
•
Pool Key = ‘CaptureTheFlag_TwoVsTwo’ or any value you'd like to use in your game.
Mode = ‘Quickmatch’
Users per Match = ‘2’ for Min Users. ‘4’ for Min Preferred. ‘6’ for Max Preferred. ‘8’ for Max Users.
Skill Pool = ‘None’
• Advanced Quickmatch = ‘Yes’
1.
2.
3.
4.
5.
Can people create and enqueue rooms themselves? - Yes
Can the system create rooms to match people into? - Yes
Can unmatched people join matchmaking rooms? - Yes
Can the matchmaking service keep matching into the same room? - Yes
Enable host migration when the room owner leaves the room? - No (Please review the Room
Ownership section below for more information about host migration. This feature requires an
additional integration.)
• Should Consider Ping Time? - ‘No’
• When you're finished entering the pool configurations, select 'Save and Deploy' to save the pool.
44 | Matchmaking | Oculus Platform
2. After you've created your pool, you can begin integrating into your client-side app. You may need to
support multiple scenarios depending on the configuration settings you chose when creating your Pool
and how the user wants to use the matchmaking service. Our pool configuration allows both users and the
system to create rooms, so we'll need to be able to handle both scenarios.
• If a user wants to join a match:
1. Call ovr_Matchmaking_Enqueue2. The user will be continually re-enqueued until they are
successfully matched or cancel the process.
2. Handle the notification that the user was enqueued, ovrMessage_Matchmaking_Enqueue2.
Information about how the Platform SDK uses notifications can be found on the Requests and
Messages page.
3. (Optional) Review the methods in the OVR_MatchmakingEnqueueResult.h file for a list of
methods you can call for information about the health of the queue to display to the user.
4. Be listening for the match notification, ovrMessage_Notification_Matchmaking_MatchFound.
5. In your notification handler, call ovr_Room_Join2 to place the user into the room identified in the
MatchFound notificaiton.
• If the user wants to create a Room and host a match:
1. To create and enqueue the user and Room, call either
ovr_Matchmaking_CreateAndEnqueueRoom2 or ovr_Matchmaking_CreateRoom2, handle
the response, and then call ovr_Matchmaking_EnqueueRoom2. The Room will be continually reenqueued until canceled or a match is found.
2. Handle the ovrMessage_Matchmaking_CreateAndEnqueueRoom2 or
ovrMessage_Matchmaking_EnqueueRoom2 response.
3. (Optional) Review the methods in the OVR_MatchmakingEnqueueResult.h file for a list of
methods you can call for information about the health of the queue to display to the user.
4. The Matchmaking service will search for matches.
3. Your game will be listening for ovrNotification_Room_RoomUpdate for the number of users in the
room. When the desired number of users is reached, your app will launch the game.
4. At any time in the matchmaking process, the user can call ovr_Matchmaking_Cancel2() to remove
themselves from the queue and exit the matchmaking process.
Browse
Finally, lets create a simple game where you allow users to browse and select the match that they want to join.
1. Create your matchmaking pool, in the Matchmaking section of the Developer Center, with the following
configurations:
• Pool Key = ‘FreeForAll_Browse’ or any value you'd like to use in your game.
• Mode = ‘Browse’
• Users per Match = ‘2’ for Min Users. ‘8’ for Max Users. (We don't specify Min or Max Preferred users for
browse. The user will define this when they create the room.)
• Skill Pool = ‘None’
• Should Consider Ping Time? = ‘No’
• When you're finished entering the pool configurations, select 'Save and Deploy' to save the pool.
2. After you've created your pool, you can begin integrating into your client-side app. Your Browse integration
will need to support multiple scenarios depending on the configuration settings you chose when creating
your Pool and how the user wants to use the service.
• If the user wants to join a match:
1. Call ovr_Matchmaking_Browse2 yo get a list of the available rooms.
Oculus Platform | Matchmaking | 45
2. Handle the ovrMessage_Matchmaking_Browse2 response and display the list of rooms to the
user. You may wish to periodically refresh the list by periodically calling the browse method.
3. Call ovr_Room_Join2 when the user has chosen a room.
• If the user wants to create a Room and host a match:
1. To create and enqueue the user and Room, call either
ovr_Matchmaking_CreateAndEnqueueRoom2 or call ovr_Matchmaking_CreateRoom2, handle
the response, and then call ovr_Matchmaking_EnqueueRoom2.
2. Handle the ovrMessage_Matchmaking_CreateAndEnqueueRoom2 or
ovrMessage_Matchmaking_EnqueueRoom2 response.
3. (Optional) Review the methods in the OVR_MatchmakingEnqueueResult.h file for a list of
methods you can call for information about the health of the queue to display to the user.
3. Your game will be listening for ovrNotification_Room_RoomUpdate for the number of users in the
room. When the desired number of users is reached, your app will launch the game.
4. At any time in the matchmaking process, the user can call ovr_Matchmaking_Cancel2() to remove
themselves from the queue and exit the matchmaking process.
Room Ownership
Each user-generated matchmaking room is associated with the user who created the room. That user who
created the room becomes the room's owner. This role may have the permission to update the settings of the
room, if you have configured the pool to allow this. During the course of the multiplayer session, it may be
necessary to transfer ownership of the room. The requests to update room data and transfer ownership can be
found on the Rooms on page 58 page.
If the room owner leaves without transferring ownership, your pool may be configured to automatically transfer
ownership to the user who has been in the room the longest. Enable this setting by choosing "Yes" for 'Enable
host migration when the room owner leaves the room?' when creating your pool.
What's next?
Once you've finished the basic Matchmaking integration, please review the Adding Skill and Using Queries on
page 45 page for information on more advanced Matchmaking integrations.
Adding Skill and Using Queries
Once you have your basic matchmaking integration working as expected, you can start adding the other
aspects of the matchmaking service to ensure that you are bringing the right users together.
This page will walk you through how to use add Skill and Matchmaking Queries to your game. We introduced
the concept of both in the Configuration Overview on page 37, with a description of the capability. This
page will review the high-level implementation.
Adding Skill Matches
Adding the Skill component to the matchmaking service is straightforward. In the Developer Center, navigate
to the Matchmaking page and create your skill pool as described in the matchmaking overview.
Once you’ve entered the information for your skill pool, the next step is to add it to your matchmaking pool.
Navigate to the pool you created for your game and select ‘View/Edit Pool’, then select the skill pool you
created from the dropdown and save your matchmaking pool.
Once you've added the skill pool to your pool, you can integrate the skill component into your game. To do
this you'll need to integrate two SDK methods to your game.
46 | Matchmaking | Oculus Platform
First, at the beginning of a match, all users in the match should call:
• Native - ovr_Matchmaking_StartMatch()
• Unity - Platform.Matchmaking.StartMatch()
Review the ovr_Matchmaking_StartMatch page for information about the parameters and return data structure.
Then, at the conclusion of the match, all users in the game should call the following method with the results of
the match:
• Native - ovr_Matchmaking_ReportResultInsecure()
• Unity - Platform.Matchmaking.ReportResultInsecure()
Review the ovr_Matchmaking_ReportResultInsecure page for information about the parameters and return data
structure.
The matchmaking service will now track and account for the user’s skills when determining the quality of a
match.
Adding Matchmaking Queries and Other Enqueue-Time Data
You can further configure the matchmaking service and use the Matchmaking Queries to compare potential
matches. Some Matchmaking requests accept an optional ovrMatchmakingOptionsHandle that allow you
to pass Data Settings and other enqueue specifics to be used when finding matches.
Defining Data Settings and Matchmaking Queries
First we’ll create the Data Setting and Matchmaking Queries that we want to use to match users. Navigate to
Matchmaking in the Developer Center and select ‘Manage Queries’ as described in Configuration Overview on
page 37.
We’ll use an example to demonstrate how Data Settings and Matchmaking Queries can be used. In the
Developer Center we’ll navigate to our matchmaking pool, which for the detailed example below we’ll call
‘my_pool’, and enter the following Data Settings:
• player_level (INTEGER)
• game_mode (STRING)
• map_size (INTEGER_BITSET)
Then we’ll create a Matchmaking Query called ‘my_query’, also in ‘my_pool’, with the following query
expressions:
• Their "player_level" is equal to my "player_level". Importance: Medium
• Their "game_mode" is equal to my "game_mode". Importance: Required
• Their "map_size" is a bitmask AND of my "map_size". Importance: Required
In the game, "map_size" bitmask has the following bit meanings:
• 0x4: large map size
• 0x2: medium map size
• 0x1: small map size
Integrating the Matchmaking Queries
First you’ll need to create an instance of the ovrMatchmakingOptionsHandle by calling
ovr_MatchmakingOptions_Create(). When you’re finished with the handle, you can call
ovr_MatchmakingOptions_Destroy() to free the memory.
After you’ve created the handle, you’ll populate the user or room enqueue message with the data settings for
the room or user. The available data settings for users and rooms are:
Oculus Platform | Matchmaking | 47
Setting Data for a Room
Setting data for a Room during the create room process is available when calling CreateRoom2 and
CreateAndEnqueueRoom2.
• ovr_MatchmakingOptions_SetCreateRoomMaxUsers will override the value of "Max Users" for a Pool
of the Developer Dashboard. Note: You can not exceed the ‘Max Users’ value that you set when creating
your Pool.
• ovr_MatchmakingOptions_SetCreateRoomJoinPolicy will specify a join policy for the created room.
If not provided, the join policy defaults to everyone.
Setting Data for a User
Setting data for a user during the enqueue processes is available when calling Enqueue2, EnqueueRoom2, and
Browse2.
• ovr_MatchmakingOptions_AddEnqueueAdditionalUser sets additional users, using their userID, at
enqueue-time as users to be added to a multiplayer session. Additional users will not receive notifications
when they are enqueued, only when a match is made.
•
•
•
•
•
Note: Once the users are matched, you may then want to team people up
based on their original groupings. You would loop through the matched
users using ovr_Room_GetMatchedUsers. Then, within each of those, call
ovr_MatchmakingEnqueuedUser_GetAdditionalUserIDsSize. If anybody has more than 1,
loop through those using ovr_MatchmakingEnqueuedUser_GetAdditionalUserID, and place
those users on the same team.
ovr_MatchmakingOptions_SetEnqueueDataSettingsInt sets an integer Data Setting.
ovr_MatchmakingOptions_SetEnqueueDataSettingsDouble sets a double Data Setting.
ovr_MatchmakingOptions_SetEnqueueDataSettingsString sets a string Data setting.
ovr_MatchmakingOptions_SetEnqueueIsDebug if true, debug information is returned with the
response payload. See "Debugging" section for more information.
ovr_MatchmakingOptions_SetEnqueueQueryKey specifies a specific Matchmaking Query for filtering
potential matches.
Example Integration Setting Enqueue-Time Data
Using the example data we defined earlier, the following example shows a user enqueueing in the
matchmaking service and looking to be matched with other players with Data Settings player_level=10,
game_mode="CaptureTheFlag", and map_size is large, medium, or both.
ovrMatchmakingOptionsHandle matchmakingOptions = ovr_MatchmakingOptions_Create();
ovr_MatchmakingOptions_SetEnqueueDataSettingsInt(matchmakingOptions, "player_level", 10);
ovr_MatchmakingOptions_SetEnqueueDataSettingsString(matchmakingOptions, "game_mode",
"CaptureTheFlag");
// I want large or medium map size
ovr_MatchmakingOptions_SetEnqueueDataSettingsInt(matchmakingOptions, "map_size", 0x4 & 0x2);
// Specify which Matchmaking Query to use with the Data Settings we provided
ovr_MatchmakingOptions_SetEnqueueQueryKey(matchmakingOptions, "my_query");
ovr_Matchmaking_Enqueue2("my_pool", matchmakingOptions);
// Destroy the matchmaking options now that we are done with it
ovr_MatchmakingOptions_Destroy(matchmakingOptions);
Testing your Matchmaking integration
When you're finished configuring your matchmaking integration, you may need to do some troubleshooting
to make sure that the service is making the matches you want it to. Please review Testing and Tuning on page
48 page for more information.
48 | Matchmaking | Oculus Platform
Testing and Tuning
To debug what's going on with your matchmaking pool, you can get snapshots of the queues using
ovr_Matchmaking_GetAdminSnapshot, which returns the state of the queue at whatever time this request
is made. This endpoint is not intended to be called in production.
// In your code, do a matchmaking enqueue first
// We can now inspect the queue to debug it.
ovr_Matchmaking_GetAdminSnapshot();
// In your handler
case ovrMessage_Matchmaking_GetAdminSnapshot:
if (!ovr_Message_IsError(message)) {
auto snapshot = ovr_Message_GetMatchmakingAdminSnapshot(message);
auto candidates = ovr_MatchmakingAdminSnapshot_GetCandidates(snapshot);
auto firstCandidate = ovr_MatchmakingAdminSnapshotCandidateArray_GetElement(candidates, 0);
if (ovr_MatchmakingAdminSnapshotCandidate_GetCanMatch(firstCandidate)) {
cout << "Yay!" << endl;
}
}
You can also view queue snapshots at enqueue time:
ovrMatchmakingOptionsHandle matchmakingOptions = ovr_MatchmakingOptions_Create();
ovr_MatchmakingOptions_SetIsDebug(matchmakingOptions, true);
// set other matchmaking options here ...
ovr_Matchmaking_Enqueue2("my_pool", matchmakingOptions);
ovr_MatchmakingOptions_Destroy(matchmakingOptions);
// In your handler
case ovrMessage_Matchmaking_Enqueue2:
if (!ovr_Message_IsError(message)) {
auto enqueueResults = ovr_Message_GetMatchmakingEnqueueResult(message);
auto snapshot = ovr_MatchmakingEnqueueResult_GetAdminSnapshot(enqueueResults);
auto candidates = ovr_MatchmakingAdminSnapshot_GetCandidates(snapshot);
auto firstCandidate = ovr_MatchmakingAdminSnapshotCandidateArray_GetElement(candidates, 0);
if (ovr_MatchmakingAdminSnapshotCandidate_GetCanMatch(firstCandidate)) {
cout << "Yay!" << endl;
}
}
Tuning Your Matches
After you've finished implementing Matchmaking and have the desired configurations in place, you may
wish to tune the Matchmaking service. We introduced the concept of Advanced Tuning and Match Quality in
Configuration Overview on page 37 where you can choose the 'Minimum Quality Bar'. We recommend
leaving this value at the default 0.5 when you were implementing Matchmaking. However, now it may be
appropriate to tune this value.
To tune your Matchmaking implementation, we recommend the following adjustments:
• Small user base - If your pool of available matchmaking players is small, you may want to reduce the
Minimum Quality Bar to allow for more matches.
• Match process takes too long - If the matchmaking service is taking too long to match users, you may want
to reduce the Minimum Quality Bar to allow more, less optimal, matches to be made.
• Large user base - If your pool of available matchmaking players is large, you may want to increase the
Minimum Quality Bar to search for better quality matches.
• Match quality is low - If low quality matches are impacting the user experience, you may want to increase the
Minimum Quality Bar to search for better quality matches.
Oculus Platform | Matchmaking | 49
Test Multiplayer Integrations Locally
The Platform SDK allows you to test your Multiplayer integration by running multiple copies of the app locally,
without making any external connections. Please see the Initializing and Checking Entitlements guide for
information about initializing in standalone mode.
Create test users in the Developer Center under the 'Settings' tab, to test matching of users into a match
locally.
50 | Peer-to-Peer Networking | Oculus Platform
Peer-to-Peer Networking
Peer-to-Peer (P2P) networking allows your app to establish a connection and send data directly between users.
There are a number of uses for P2P networking. Some common implementations include, using P2P to
exchange chat messages between users, updating gameplay data in lower latency, or update avatar positions
in real-time. Our P2P service connect users to exchange data directly.
The Oculus Platform P2P allows you to choose between two send policies to transmit data depending on your
use-case and the needs of your app.
• Reliable connections transmit data that will be delivered once and in the order that messages are sent.
The networking layer retries until each message is acknowledged by the peer. Outgoing messages are
buffered until a working connection to the peer is established. Reliable messages are useful when using P2P
to send text chat messages that need ot be delivered once and in the order that the user sent them, but not
necessarily instantaneously.
• Unreliable connections transmit data as best-effort, but cannot guarantee delivery in a specific order or timeframe. Any messages sent before a connected state is established will be dropped. Unreliable messages
are useful when you're using P2P to update game data in realtime and transmitting data quickly is more
important than guaranteeing delivery once and in order.
Integrate Peer-to-Peer Networking
The following methods are available to call from your client app:
• Establish a connection:
Native - ovr_Net_Connect()
Unity - Platform.Net.Connect()
This sends a ovrMessage_NetworkingPeerConnectRequest to the specified user. It returns a
ovrMessage_NetworkingPeerConnectionStateChange notification when the connection is
established, you’ll need to be listening for this notification. Review the ovr_Net_Connect page for
information about the parameters and return data structure.
• Accept and open a connection:
Native - ovr_Net_Accept()
Unity - Platform.Net.Accept()
This should be called after receiving a ovrMessage_NetworkingPeerConnectRequest message.
Review the ovr_Net_Accept page for information about the parameters and return data structure.
• Accept and open a connection for a room:
Native - ovr_Net_AcceptForCurrentRoom()
Unity - Platform.Net.AcceptForCurrentRoom()
Accept connection attempts from members of the current room. Please see the Rooms on page 58 page
for information about creating and joining rooms. Returns false if the user currently isn't in a room. Review
the ovr_Net_AcceptForCurrentRoom page for information about the parameters and return data structure.
• Check a connection with a user:
Native - ovr_Net_IsConnected()
Unity - Platform.Net.IsConnected()
Oculus Platform | Peer-to-Peer Networking | 51
Checks if the current users is connected to another specific user, returns a true or false. Review the
ovr_Net_IsConnected page for information about the parameters and return data structure.
• Ping a user:
Native - ovr_Net_Ping()
Unity - Platform.Net.Ping()
Ping a user to determine network connection quality. Review the ovr_Net_Ping page for information about
the parameters and return data structure.
• Send data to a user:
Native - ovr_Net_SendPacket()
Unity - Platform.Net.SendPacket()
Send a sequence of bytes to another user. The length must be less than or equal to the allocated length of
bytes. A new connection to userID will be established (asynchronously) unless one already exists. Review the
ovr_Net_SendPacket page for information about the parameters and return data structure.
• Send data to a Room:
Native - ovr_Net_SendPacketToCurrentRoom()
Unity - Platform.Net.SendPacketToCurrentRoom()
Sends a packet to all members of the room, excluding the currently logged in user. The room has to
be created or joined by calling one of the room/matchmaking functions. See ovr_Net_SendPacket for a
description of parameters. This function returns false if the user currently isn't in a room.
• Read a data packet:
Native - ovr_Net_ReadPacket()
Unity - Platform.Net.ReadPacket()
Read the next incoming data packet. The return handle will point to an object that represents
some data read from the network. This returns the ovrMessage_ReadPacket message type. Use
ovr_Packet_GetSenderID() to get the sender ID, ovr_Packet_GetSize to get the size of the packet,
or ovr_Packet_GetBytes to get the contents of the packet. Review the ovr_Net_ReadPacket page for
information about the parameters and return data structure.
• Release the data packet from memory:
Native - ovr_Packet_Free()
Unity - Platform.Packet.Free()
Once the message has been read, release the packet and free the memory used by the packet. Review the
ovr_Packet_Free page for information about the parameters and return data structure.
• Close a connection:
Native - ovr_Net_Close()
Unity - Platform.Net.Close()
When the session is complete, close the connection. Review the ovr_Net_Close page for information about
the parameters and return data structure.
• Close a connection for a Room:
Native - ovr_Net_CloseForCurrentRoom()
Unity - Platform.Net.CloseForCurrentRoom()
52 | Peer-to-Peer Networking | Oculus Platform
Close the connection with everyone in the current room. Call this before leaving the room. Review the
ovr_Net_CloseForCurrentRoom page for information about the parameters and return data structure.
Listening for Messages
Integrating Peer-to-Peer networking involves handling the notifications that are sent as part of the networking
process. These notifications could be connection requests and notifications that the state has changed. Please
review the Requests and Messages page for more information about how notifications work in the Platform
SDK.
Specifically, for P2P you should be listening and able to handle the following notifications:
• ovrMessage_Notification_Networking_ConnectionStateChange
• ovrMessage_Notification_Networking_PeerConnectRequest
• ovrMessage_Notification_Networking_PingResult
Example Implementation
In general, a P2P connection between two users, we'll call them A and B, would follow:
1. Initiate a connection between two users. Explicitly connecting users by calling ovr_Net_Connect()
ensures that all packets will received. You can skip this step and allow the service to implicitly connect
when the first packet is received. Unreliable messages received before a connection, and a state change to
ovrPeerState_Connected, is established may be dropped.
a. User A sends a connection request by calling ovr_Net_Connect() with the user B's userID.
b. User B calls ovr_Net_Accept() to accept and initiate the networking session.
2. Both users receive the ovrMessage_NetworkingPeerConnectionStateChange notification that the
state has changed to ovrPeerState_Connected. User A can now call ovr_Net_SendPacket() to send
the message or data. When calling ovr_Net_SendPacket() you'll define one of the policies described
above, reliable or unreliable. Please see ovrSendPolicy for more information.
3. User B's application parses the message using ovr_Net_ReadPacket() and ovr_Packet_GetBytes().
The application frees the memory used by the message on both clients with ovr_Packet_Free().
4. (Optional) Destroy the connection by calling ovr_Net_Close(). The SDK manages the pool of
connections and will discard unused connections.
The following example comes from the VRVoiceChat Sample App, a Unity app that uses P2P to transmit realtime avatar location and movement of other users.
More information about Avatars can be found in the Avatar SDK documentation.
using
using
using
using
UnityEngine;
System;
Oculus.Platform;
Oculus.Platform.Models;
// Helper class to manage a Peer-to-Peer connection to the other user.
// The connection is used to send and received the Transforms for the
// Avatars. The Transforms are sent via unreliable UDP at a fixed
// frequency.
public class P2PManager
{
// number of seconds to delay between transform updates
private static readonly float UPDATE_DELAY = 0.1f;
// the ID of the remote player we expect to be connected to
private ulong m_remoteID;
// the result of the last connection state update message
private PeerConnectionState m_state = PeerConnectionState.Unknown;
// the next time to send an updated transform to the remote User
private float m_timeForNextUpdate;
Oculus Platform | Peer-to-Peer Networking | 53
// the size of the packet we are sending and receiving
private static readonly byte PACKET_SIZE = 29;
// packet format type just in case we want to add new future packet types
private static readonly byte PACKET_FORMAT = 0;
// reusable buffer to serialize the Transform into
private readonly byte[] sendTransformBuffer = new byte[PACKET_SIZE];
// reusable buffer to deserialize the Transform into
private readonly byte[] receiveTransformBuffer = new byte[PACKET_SIZE];
// the last received position update
private Vector3 receivedPosition;
// the previous received position to interpolate from
private Vector3 receivedPositionPrior;
// the last received rotation update
private Quaternion receivedRotation;
// the previous received rotation to interpolate from
private Quaternion receivedRotationPrior;
// when the last transform was received
private float receivedTime;
public P2PManager(Transform initialHeadTransform)
{
receivedPositionPrior = receivedPosition = initialHeadTransform.localPosition;
receivedRotationPrior = receivedRotation = initialHeadTransform.localRotation;
}
Net.SetPeerConnectRequestCallback(PeerConnectRequestCallback);
Net.SetConnectionStateChangedCallback(ConnectionStateChangedCallback);
#region Connection Management
public void ConnectTo(ulong userID)
{
m_remoteID = userID;
}
// ID comparison is used to decide who calls Connect and who calls Accept
if (PlatformManager.MyID < userID)
{
Net.Connect(userID);
}
public void Disconnect()
{
if (m_remoteID != 0)
{
Net.Close(m_remoteID);
m_remoteID = 0;
m_state = PeerConnectionState.Unknown;
}
}
public bool Connected
{
get
{
return m_state == PeerConnectionState.Connected;
}
}
void PeerConnectRequestCallback(Message<NetworkingPeer> msg)
{
Debug.LogFormat("Connection request from {0}, authorized is {1}", msg.Data.ID, m_remoteID);
}
if (msg.Data.ID == m_remoteID)
{
Net.Accept(msg.Data.ID);
}
void ConnectionStateChangedCallback(Message<NetworkingPeer> msg)
{
54 | Peer-to-Peer Networking | Oculus Platform
Debug.LogFormat("Connection state to {0} changed to {1}", msg.Data.ID, msg.Data.State);
if (msg.Data.ID == m_remoteID)
{
m_state = msg.Data.State;
}
}
if (m_state == PeerConnectionState.Timeout &&
// ID comparison is used to decide who calls Connect and who calls Accept
PlatformManager.MyID < m_remoteID)
{
// keep trying until hangup!
Net.Connect(m_remoteID);
}
PlatformManager.SetBackgroundColorForState();
#endregion
#region Send Update
public bool ShouldSendHeadUpdate
{
get
{
return Time.time >= m_timeForNextUpdate && m_state == PeerConnectionState.Connected;
}
}
public void SendHeadTransform(Transform headTransform)
{
m_timeForNextUpdate = Time.time + UPDATE_DELAY;
sendTransformBuffer[0] = PACKET_FORMAT;
int offset = 1;
PackFloat(headTransform.localPosition.x,
PackFloat(headTransform.localPosition.y,
PackFloat(headTransform.localPosition.z,
PackFloat(headTransform.localRotation.x,
PackFloat(headTransform.localRotation.y,
PackFloat(headTransform.localRotation.z,
PackFloat(headTransform.localRotation.w,
}
sendTransformBuffer,
sendTransformBuffer,
sendTransformBuffer,
sendTransformBuffer,
sendTransformBuffer,
sendTransformBuffer,
sendTransformBuffer,
ref
ref
ref
ref
ref
ref
ref
offset);
offset);
offset);
offset);
offset);
offset);
offset);
Net.SendPacket(m_remoteID, sendTransformBuffer, SendPolicy.Unreliable);
void PackFloat(float f, byte[] buf, ref int offset)
{
Buffer.BlockCopy(BitConverter.GetBytes(f), 0, buf, offset, 4);
offset = offset + 4;
}
#endregion
#region Receive Update
public void GetRemoteHeadTransform(Transform headTransform)
{
bool hasNewTransform = false;
Packet packet;
while ((packet = Net.ReadPacket()) != null)
{
if (packet.Size != PACKET_SIZE)
{
Debug.Log("Invalid packet size: " + packet.Size);
continue;
}
packet.ReadBytes(receiveTransformBuffer);
}
if (receiveTransformBuffer[0] != PACKET_FORMAT)
{
Debug.Log("Invalid packet type: " + packet.Size);
continue;
}
hasNewTransform = true;
Oculus Platform | Peer-to-Peer Networking | 55
if (hasNewTransform)
{
receivedPositionPrior = receivedPosition;
receivedPosition.x = BitConverter.ToSingle(receiveTransformBuffer, 1);
receivedPosition.y = BitConverter.ToSingle(receiveTransformBuffer, 5);
receivedPosition.z = BitConverter.ToSingle(receiveTransformBuffer, 9);
receivedRotationPrior = receivedRotation;
receivedRotation.x = BitConverter.ToSingle(receiveTransformBuffer,
receivedRotation.y = BitConverter.ToSingle(receiveTransformBuffer,
receivedRotation.z = BitConverter.ToSingle(receiveTransformBuffer,
receivedRotation.w = BitConverter.ToSingle(receiveTransformBuffer,
}
}
}
13);
17) * -1.0f;
21);
25) * -1.0f;
receivedTime = Time.time;
// since we're receiving updates at a slower rate than we render,
// interpolate to make the motion look smoother
float completed = Math.Min(Time.time - receivedTime, UPDATE_DELAY) / UPDATE_DELAY;
headTransform.localPosition =
Vector3.Lerp(receivedPositionPrior, receivedPosition, completed);
headTransform.localRotation =
Quaternion.Slerp(receivedRotationPrior, receivedRotation, completed);
#endregion
56 | Parties | Oculus Platform
Parties
Parties allow users to chat with friends and navigate VR as a group.
Parties are only available for Gear VR apps at this time.
Users can create Parties, start a voice chat using Voice Chat (VoIP) on page 67, invite friends to join them in
Rooms on page 58, and even invite their Party to join an app using Coordinated App Launch (CAL) on page
18. Party voice chat persists across apps in VR and users can continue to interact while navigating between
apps.
You don't have to do anything to allow users to join Parties; however, there are a couple steps if you are
planning to use the microphone for commands or chat in your app.
• If you're using Voice Chat (VoIP) on page 67 - In addition to integrating the VoIP service, you also need
to integrate with our SystemVoIP API. Note: Parties introduces a change that removes echo cancellation
from our VoIP inputs. We will be adding this back in as an option in the future. In the meantime, you may
want to require users to use headphones and mute a user's mic if they don't have headphones plugged in.
• If you're using your own VoIP service - You need to integrate with both the Shared Mic API and SystemVoIP
API.
• If you're using the mic, but not for VoIP - If you're using the mic for something other than VoIP (e.g. voice
search or voice commands), you need to integrate with our Shared Mic API.
Shared Microphone API
The Shared Microphone API allows sharing of mic access between multiple services so app can access the user
mic data while in a Party.
To integrate with our API, you need to instantiate our mic object and retrieve PCM data from it.
// This file was @generated with LibOVRPlatform/codegen/main. Do not modify it!
#ifndef OVR_MICROPHONE_H
#define OVR_MICROPHONE_H
#include "OVR_Platform_Defs.h"
#include <stdint.h>
#include <stddef.h>
typedef struct ovrMicrophone *ovrMicrophoneHandle;
/// Creates an ovrMicrophone object and returns a handle to it.
/// The caller owns this memory and is responsible for freeing it.
/// Use ovr_Microphone_Destroy to free the object.
OVRP_PUBLIC_FUNCTION(ovrMicrophoneHandle) ovr_Microphone_Create();
/// Stops and frees a previously created microphone object.
OVRP_PUBLIC_FUNCTION(void) ovr_Microphone_Destroy(ovrMicrophoneHandle obj);
/// Gets all available samples of microphone data and copies it into
/// outputBuffer. The microphone will generate data at roughly the rate of 480
/// samples per 10ms. The data format is 16 bit fixed point 48khz mono.
///
/// This function can be safely called from any thread.
OVRP_PUBLIC_FUNCTION(size_t) ovr_Microphone_GetPCM(const ovrMicrophoneHandle obj, int16_t
*outputBuffer, size_t outputBufferNumElements);
/// Gets all available samples of microphone data and copies it into
/// outputBuffer. The microphone will generate data at roughly the rate of 480
/// samples per 10ms. The data format is 32 bit floating point 48khz mono.
///
/// This function can be safely called from any thread.
OVRP_PUBLIC_FUNCTION(size_t) ovr_Microphone_GetPCMFloat(const ovrMicrophoneHandle obj, float
*outputBuffer, size_t outputBufferNumElements);
/// Starts microphone recording. After this is called pcm data can be extracted
Oculus Platform | Parties | 57
/// using ovr_Microphone_GetPCM.
///
/// This function can be safely called from any thread.
OVRP_PUBLIC_FUNCTION(void) ovr_Microphone_Start(const ovrMicrophoneHandle obj);
/// Stops microphone recording.
///
/// This function can be safely called from any thread.
OVRP_PUBLIC_FUNCTION(void) ovr_Microphone_Stop(const ovrMicrophoneHandle obj);
// DEPRECATED - use ovr_Microphone_GetPCMFloat instead
OVRP_PUBLIC_FUNCTION(size_t) ovr_Microphone_ReadData(const ovrMicrophoneHandle obj, float
*outputBuffer, size_t outputBufferSize);
#endif
SystemVoIP API
The SystemVoIP API ensures that the user doesn't hear both the Party VoIP and the VoIP in your app at the
same time.
There are two ways to check the state of SystemVoIP (active means the user is using Party VoIP). One way is to
check every frame, the other is to listen for a notification.
1. Check Every Frame - To check whether SystemVoIP is active at any time:
• In C: ovr_Voip_GetSystemVoipStatus() == ovrSystemVoipStatus_Active
• In C#: Voip.GetSystemVoipStatus() == SystemVoipStatus.Active
These are synchronous functions, not requests.
2. Listen for a Notification - To get a notification for changes in the VoIP state you can make the following
request:
• In C: Watch for ovrMessage_Notification_Voip_SystemVoipState, and then use
ovr_Message_GetSystemVoipState() and ovr_SystemVoipState_GetStatus() to get the
status.
• In C# Watch for MessageType.Notification_Voip_SystemVoipState which has
a Message<SystemVoipState> from which you can access the Status field, or call
SetSystemVoipStateNotificationCallback with a Message<SystemVoipState> callback
It's possible for the state to change quickly. The values you extract from notification messages will be the
state at the time the notification was added to the message queue, which may be different from the state
then the message is processed. You may wish to listen for the notification, and then ignore its values and
check the current state using the synchronous functions.
If the SystemVoIP is active you can:
• Use In-App VoIP - i.e. you want the user to be in your in-app chat instead of in Party VoIP. Suppress
SystemVoIP by calling ovr_Voip_SetSystemVoipSuppressed(bool suppressed) and use either the
Shared Microphone API or the Platform VoIP API to run your in-app VoIP.
• Use SystemVoIP (Party VoIP) - i.e. you want to use Party VoIP instead of in your in-app VoIP. Suppress your
in-app VoIP (don't send the user's mic input to other users and don't play other user's audio to the user).
SystemVoIP will be unsupressed if the if the app has it suppressed quits.
You may also allow your users to toggle between SystemVoIP and your in-app VoIP. This allows users to choose
if they want to keep talking to their party (in which case you would need to suppress your in-app VoIP), or if
they want to use your in-app VoIP (in which case you would need to suppress the SystemVoIP).
58 | Rooms | Oculus Platform
Rooms
Rooms are virtual places where users come together to interact in your app.
Once together in a room, users can navigate your VR application together, chat, and even launch a
Matchmaking session from certain types of rooms (chat and Matchmaking will require separate integrations with
Voice Chat (VoIP) on page 67 and Matchmaking on page 37).
There are three types of rooms available to implement in your game:
• User-created private rooms are created by users to interact in VR. Rooms are created and owned by a user,
they invite friends join the room where they interact, and then when everyone has left the room is destroyed.
• User-created matchmaking rooms are used by the Matchmaking on page 37 service to bring users together
to interact and enqueue in the Matchmaking Service to find a multiplayer game session.
• Moderated rooms are persistent, application hosted, rooms that your application creates and maintains.
Once you’ve created the rooms, or allowed your users to create rooms themselves, use the room invites
methods to help users to find their friends and add them to the rooms.
Note: The Platform SDK Rooms feature should not to be confused with the Rooms App available for
Gear VR.
Integrating Rooms
The process to integrate rooms is different depending on what kind of experience you want your users to have.
We’ll review the integration for both user-created private rooms and system-owned moderated rooms in this
section, information about integrating user-created matchmaking rooms can be found in the Matchmaking on
page 37 guide. Then we'll introduce the methods you can call to interact with all rooms.
When integrating rooms, be sure to familiarize yourself with how Requests and Messages work in the Platform
SDK. Notifications are a central component of the Rooms feature.
Integrate User-Created Private Rooms
This section details the methods that are available when creating and updating private user-generated rooms.
Some methods listed below can only be called by the Room Owner. The owner is the user who created the
room. If the user who created the room leaves, ownership of the room will be transferred to the person who has
been in the room the longest. Room ownership can also be transferred by calling ovr_Room_UpdateOwner().
• Create a Private User-Generated Room:
Native - ovr_Room_CreateAndJoinPrivate2()
Unity - Platform.Room.CreateAndJoinPrivate2()
Rooms created with this method can not be promoted to a matchmaking room. If you want to allow users
to enqueue the room as a matchmaking room, you should use the ovr_Matchmaking_CreateRoom2
method. Please see Matchmaking on page 37 for more information about creating matchmaking rooms.
Review the ovr_Room_CreateAndJoinPrivate2 page for information about the parameters and return data
structure.
• Kick a User out of a Room:
Native - ovr_Room_KickUser()
Unity - Platform.Room.KickUser()
Oculus Platform | Rooms | 59
This method can only be called by the room owner. Review the ovr_Room_KickUser page for information
about the parameters and return data structure.
• Update Room Datastore:
Native - ovr_Room_UpdateDataStore()
Unity - Platform.Room.UpdateDataStore()
This method can only be called by the room owner. Add up to 2,000 key/value pairs to the room metadata.
Review the ovr_Room_UpdateDataStore page for information about the parameters and return data
structure.
• Update Room Join Policy:
Native - ovr_Room_UpdatePrivateRoomJoinPolicy()
Unity - Platform.Room.UpdatePrivateRoomJoinPolicy()
Update the room join policy, or the definition of who can join the room. This method can only be called
by the room owner. Review the ovr_Room_UpdatePrivateRoomJoinPolicy page for information about the
parameters and return data structure.
• Update Room Membership Lock Status:
Native - ovr_Room_UpdateMembershipLockStatus()
Unity - Platform.Room.UpdateMembershipLockStatus()
Call this method to lock the membership of the room, calling this and setting to true will prevent
additional users from joining the room. This method can only be called by the room owner. Review the
ovr_Room_UpdateMembershipLockStatus page for information about the parameters and return data
structure.
• Update Room Owner:
Native - ovr_Room_UpdateOwner()
Unity - Platform.Room.UpdateOwner()
This method can only be called by the room owner. Review the ovr_Room_UpdateOwner page for
information about the parameters and return data structure.
• Update Room Description:`
Native - ovr_Room_SetDescription()
Unity - Platform.Room.SetDescription()
This method can only be called by the room owner. Review the ovr_Room_SetDescription page for
information about the parameters and return data structure.
Integrate User-Created Matchmaking Rooms
Information about how to configure the matchmaking service and integrate user-created matchmaking rooms
can be found in the Matchmaking on page 37 guide.
Integrate Moderated Rooms
Moderated rooms are created through S2S REST calls. The client app should not have any interaction with
creating or maintaining the moderated room. Please review the Server-to-Server API Basics for information
about making S2S API calls, specifically about using the access tokens.
With moderated rooms, only your trusted servers can create or make changes to the rooms.
60 | Rooms | Oculus Platform
Create a Moderated Room
Calling this endpoint will create a new moderated room.
$ curl -d 'access_token=$APP_ACCESSTOKEN' -d 'max_users=MY_MAX_USER_COUNT'
https://graph.oculus.com/room_moderated_create
You’ll need to include the max_users parameter which identifies the max simultaneous users for the room.
Example response:
{"id": 963119010431337}
In response the API will return the moderated_room_id as the id value. Call this id when adding users to the
room.
Update a Moderated Room's Datastore
Calling this endpoint sets up to 2,000 key/value pairs in the room datastore. Key and value data can only be
accepted as string values, any other value type will return an error. The ROOM_ID is the value returned from the
API when you created the room.
$ curl -d 'access_token=$APP_ACCESSTOKEN' -d 'room_id=$ROOM_ID'
-d 'datastore=[{"key":"KEY","value":"VALUE"},{"key":"KEY2","value":"VALUE2"}]'
https://graph.oculus.com/room_update_data_store
Example response:
{"success": true}
Retrieve Information about a Moderated Room
Calling this endpoint will retrieve information about a specified moderated room. The ROOM_ID is the value
returned from the API when you created the room. The parameters in the example below are all optional,
information will only be returned for the parameters identified in the request.
$ curl -d 'access_token=$APP_ACCESSTOKEN' -d
'fields=max_users,owner{id,alias,presence,presence_status,profile_url,profile_url_small},
users{id,alias,presence,presence_status,profile_url,profile_url_small},
data_store,name,description,join_policy,type,joinability,version,is_membership_locked'
https://graph.oculus.com/$ROOM_ID
Delete a Moderated Room
Calling this endpoint will delete a moderated room.
$ curl -X DELETE -d 'access_token=$APP_ACCESSTOKEN'
https://graph.oculus.com/$MODERATED_ROOM_ID
Example response:
{"success": true}
Interacting with Rooms
This section describes the methods can be called to get information and interact with all types of rooms.
• Retrieve Information About a Room:
Native - ovr_Room_Get()
Oculus Platform | Rooms | 61
Unity - Platform.Room.Get()
Review the ovr_Room_Get page for information about the parameters and return data structure.
• Get Current Room Information:
Native - ovr_Room_GetCurrent()
Unity - Platform.Room.GetCurrent()
Review the ovr_Room_GetCurrent page for information about the parameters and return data structure.
• Get Current Room Information for Another User:
Native - ovr_Room_GetCurrentForUser()
Unity - Platform.Room.GetCurrentForUser()
Review the ovr_Room_GetCurrentForUser page for information about the parameters and return data
structure.
• Join a Room:
Native - ovr_Room_Join2()
Unity - Platform.Room.Join2()
Review the ovr_Room_Join2 page for information about the parameters and return data structure.
• Leave a Room:
Native - ovr_Room_Leave()
Unity - Platform.Room.Leave()
Review the ovr_Room_Leave page for information about the parameters and return data structure.
Integrating Invites
Rooms invites allow users to invite others to join them in a social VR experience. Room invites can be received
whether the invited person is currently in VR or not. However, users can only be invited to apps they’re entitled
to.
You can send invites by constructing your own custom UI or using the Oculus UI provided with the Platform
SDK.
Use a Custom UI
1. Make sure the user has joined the room for which he or she is sending invites. You can call ‘Get Current
Room Information’ to confirm.
2. Get a list of users that can be invited to that room. Call:
Native - ovr_Room_GetInvitableUsers2()
Unity - Platform.Room.GetInvitableUsers2()
You’ll get information for each inviteable person (friends of the user who also own your app) that includes
the Oculus username and a user-specific invite token.
Review the ovr_Room_GetInvitableUsers2 page for information about the parameters and return data
structure.
3. Then call to invite the selected user to the Room:
Native - ovr_Room_InviteUser()
62 | Rooms | Oculus Platform
Unity - Platform.Room.InviteUser()
Review the ovr_Room_InviteUser page for information about the parameters and return data structure.
4. If you request room updates for the room using the subscribeToUpdates parameter, your app will
receive a notification when the user joins. Be sure to listen for this notification.
Note: Each invite token can only be used once. To re-invite the same user, repeat the process and send
a new invite.
Use the Oculus UI
1. Make sure the user has joined the room for which he or she is sending invites. You can call ‘Get Current
Room Information’ to confirm.
2. To open the Oculus UI, temporarily background your app and call:
Native - ovr_Room_LaunchInvitableUserFlow()
Unity - Platform.Room.LaunchInvitableUserFlow()
Review the ovr_Room_LaunchInvitableUserFlow page for information about the parameters and return data
structure.
3. The user will see an interface outside of your app, in the SystemUI, that allows users to invite others. The
platform will take care of the rest.
Accepting an Invite and Joining a Room from Home
You can configure your app to launch invites directly from Oculus Home. This allows your users to jump directly
into a social session in your app.
To accept invites outside of your application:
1. Oculus will provide the invite notifications to the user in both Rift and Gear VR Home.
2. If the user accepts this notification Oculus will then launch your application.
3. To detect that the user launched from accepting an invite, you can look for an invite-accepted message
when launching your app.
• For a native app, when polling the message queue, look for the
ovrMessage_Notification_Room_InviteAccepted message. For example:
int messageType = ovr_Message_GetType(response);
if (messageType == ovrMessage_Notification_Room_InviteAccepted) {
const char *roomIDString = ovr_Message_GetString(response);
ovrID roomID;
ovrID_FromString(&roomID, roomIDString));
// we can now try to join the room
}
• For a Unity app, use the following callback:
Oculus.Platform.Rooms.SetRoomInviteNotificationCallback (
(Oculus.Platform.Message<string> msg) =>{
if (msg.IsError) {
// Handle error
} else {
string roomID = msg.GetString();
// we can now try to join the room
}
}
);
Room Updates
Oculus Platform | Rooms | 63
Rooms relies on notifications and the message queue for updates to the room and invite notifications , please
see Requests and Messages for information about how the queue works.
Rooms Quickstart
Now that we've reviewed the different types of rooms you can create and how you're able to interact with
them, let's review a very basic rooms integration that supports invites.
This integration will allow users to create a private room, not used for matchmaking, for a social VR experience.
1. Create the Room: A user calls ovr_Room_CreateAndJoinPrivate2() to create and join a private usercreated room. This user becomes the owner of the room.
a. Specify the joinPolicy and maxUsers for the room when sending the create request. joinPolicy
defines who is able to join the room, see RoomJoinPolicy for the available policies. maxUsers is the
maximum number of users that can be added to a room.
b. Once created, the room owner has the ability to lock the membership of the room by calling
ovr_Room_UpdateMembershipLockStatus(). This will prevent new members from being able to
join the room. Any users that are in the room at the time of lock will be able to rejoin.
2. Invite Friends: Using the pre-built Oculus invites interface is the simplest way for your app to allow users to
invite their friends. The app calls ovr_Room_LaunchInvitableUserFlow() with the roomID of the room
that was just created. The invite interface will be opened where the user may find and invite users to the
room.
3. Join the Room: Recipients of invites will receive a notification on the message queue that they have been
invited to join a room.
a. Handle the notification on the message queue and extract the roomID from the notification payload.
Please see the Requests and Messages page for information about how the Platform SDK uses messages,
notifications, and the queue.
b. The invitee's client can retrieve information about the room using ovr_Room_Get() with the roomID of
the room. The response will a Room message that contains the details about the room.
c. If the invitee decides to join the room, add them using ovr_Room_Join2() with the roomID of the
room.
d. At any time the user may call ovr_Room_Leave() to leave the room.
64 | Sharing | Oculus Platform
Sharing
The Oculus Platform allows users to share their VR experience with their Facebook network.
For users to share with their Facebook network they first need to connect their Oculus account to their
Facebook account. You may wish to encourage users to stream, to do so notify them that Sharing is available if
they link their Oculus and Facebook accounts. Account linking is available in the ‘Settings’ menu of the Oculus
2D app.
When preparing your builds for testing and release you'll have to choose whether to allow Sharing in your app.
Oculus encourages you to allow sharing as it exposes your VR experience with a large Facebook audience who
may not own your app.
To enable Sharing, navigate to Sharing on the Developer Center, check the box to indicate that you have read
and accept the terms, and select ‘Save’.
If you do not want to enable sharing, you'll still need to navigate to the Sharing page and select ‘Save’ with the
box unchecked. There is no default setting, you have to choose to enable or disable Sharing in order to publish
your app.
Note: Sharing is only available for Gear VR apps at this time.
Livestreaming
All sharing is initiated by the user through the Oculus Platform. However, there are a couple of SDK methods
you should call at certain points in your game to accommodate livestreaming. For example, you should pause
a livestream if the user is entering a password or credit card information. Then, once the user has finished that
activity, you can resume the stream.
The following SDK methods can be called from the client app.
• Check the livestream status:
Native - ovr_Livestreaming_GetStatus()
Unity - Platform.Livestreaming.GetStatus()
Review the ovr_Livestreaming_GetStatus page for information about the parameters and return data
structure.
• Pause the livestream:
Native - ovr_Livestreaming_PauseStream()
Unity - Platform.Livestreaming.PauseStream()
Review the ovr_Livestreaming_PauseStream page for information about the parameters and return data
structure.
• Resume the livestream:
Native - ovr_Livestreaming_ResumeStream()
Unity - Platform.Livestreaming.ResumeStream()
Review the ovr_Livestreaming_ResumeStream page for information about the parameters and return data
structure.
Testing Livestreaming
Oculus Platform | Sharing | 65
Livestreaming a user's experience may have an impact on the performance of your app. We recommend testing
how your app performs when a user is livestreaming and adjusting features and performance as appropriate.
To test livestreaming in your app:
1. Add the ovr_Livestreaming_GetStatus() check to your app to determine when a user has enabled
livestreaming.
2. Upload a build to a testing channel in the Developer Center. Information about release channels and
uploading builds can be found in the Distribute section of these docs. Any user who has been added to your
app's organization will be able to test livestreaming.
3. Launch the app and initiate a livestream. Monitor the performance of your app while the livestream is
running to determine if any adjustments are necessary. Please see the Mobile Rendering Guidelines page
for best practices and performance requirements when developing Gear VR apps.
4. If adjustments are required during a livestream, enable them any time a user has initiated a livestream by
checking ovr_Livestreaming_GetStatus().
66 | User and Friends | Oculus Platform
User and Friends
User and Friends manages your user’s unique persona and their relationship with their friends. Find a user’s
friends, their status in your app, and the room they're in using Friends.
Use Friends in combination with Rooms on page 58 to find help your players find their friends in VR, then place
them in a Room where they can interact in VR.
Integrating User and Friends
The following SDK methods can be called from your client app.
• Retrieve information about the current user:
Native - ovr_User_GetLoggedInUser()
Unity - Platform.User.GetLoggedInUser()
Review the ovr_User_GetLoggedInUser page for information about the parameters and return data
structure.
• Retrieve a list of the user’s friends:
Native - ovr_User_GetLoggedInUserFriends()
Unity - Platform.User.GetLoggedInUserFriends()
Review the ovr_User_GetLoggedInUserFriends page for information about the parameters and
return data structure. If there are a large number of values being returned, you may need to call
ovr_User_GetNextUserArrayPage and paginate the data.
• Retrieve a list of the user’s friends and the Room they are currently in:
Native - ovr_User_GetLoggedInUserFriendsAndRooms()
Unity - Platform.User.GetLoggedInUserFriendsAndRooms()
Review the ovr_User_GetLoggedInUserFriendsAndRooms page for information about the parameters
and return data structure. If there are a large number of values being returned, you may need to call
ovr_User_GetNextUserAndRoomArrayPage and paginate the data.
• Retrieve information about another user:
Native - ovr_User_Get()
Unity - Platform.User.Get()
Review the ovr_User_Get page for information about the parameters and return data structure.
• Retrieve an Org Scoped ID for the user:
Native - ovr_User_GetOrgScopedID()
Unity - Platform.User.GetOrgScopedID()
This method returns an ID that is consistent for a user across all applications in your organization. You can
use this ID to identify users across multiple apps. Please note that the OrgScopedID is not the same as the
userID in an app.
Review the ovr_User_GetOrgScopedID page for information about the parameters and return data structure.
Oculus Platform | Voice Chat (VoIP) | 67
Voice Chat (VoIP)
Use the Platform VoIP service to add voice chat to your app.
The VoIP service transmits PCM (pulse-code modulation) data between users that the Platform SDK decodes to
audio.
VoIP is peer-to-peer so your app will limited by bandwidth in most cases. In general, the VoIP service becomes
unstable if you have 12, or more, connections. We recommend no more than 8 connections on Rift and 4 on
Gear VR. You may be able to support more if you use push to talk or some proximity type muting.
If you're building a Gear VR app, please review Parties on page 56 for information about supporting Party VoIP.
Integrating VoIP
To integrate Application VoIP in your Native application, follow the steps below. The methods can be called
from your client app.
• Send a VoIP Connection Request:
Native - ovr_Voip_Start()
Unity - Platform.Voip.Start()
Initiate a VoIP connection request. Review the ovr_Voip_Start page for information about the parameters
and return data structure.
• Accept a VoIP Connection Request:
Native - ovr_Voip_Accept()
Unity - Platform.Voip.Accept()
Accept a VoIP connection request. Review the ovr_Voip_Accept page for information about the parameters
and return data structure.
• Retrieve the Maximum Size of the Buffer:
Native - ovr_Voip_GetOutputBufferMaxSize()
Unity - Platform.Voip.GetOutputBufferMaxSize()
Retrieve the size of the internal ringbuffer used by the VoIP system in elements. This size is the maximum
number of elements that can ever be returned by ovr_Voip_GetPCM or ovr_Voip_GetPCMFloat. Review
the ovr_Voip_GetOutputBufferMaxSize page for information about the parameters and return data structure.
• Retrieve PCM Data from a User (16 bit fixed point):
Native - ovr_Voip_GetPCM()
Unity - Platform.Voip.GetPCM()
Retrieve all available samples of voice data from a specified user and copy them into the outputBuffer. The
VoIP system will generate data at roughly the rate of 480 samples per 10ms. This function should be called
every frame with 50ms (2400 elements) of buffer size to account for frame rate variations. The data format is
16 bit fixed point 48khz mono.
Review the ovr_Voip_GetPCM page for information about the parameters and return data structure.
• Retrieve PCM Data from a User (32 bit floating point):
Native - ovr_Voip_GetPCMFloat()
68 | Voice Chat (VoIP) | Oculus Platform
Unity - Platform.Voip.GetPCMFloat()
Retrieve all available samples of voice data from a specified user and copy them into the outputBuffer The
VoIP system will generate data at roughly the rate of 480 samples per 10ms. This function should be called
every frame with 50ms (2400 elements) of buffer size to account for frame rate variations. The data format is
32 bit floating point 48khz mono.
Review the ovr_Voip_GetPCMFloat page for information about the parameters and return data structure.
We do not recommend using the floating point methods unless necessary. This operation requires
additional resources when compared to the integer based process.
• Retrieve the Number of PCM Data Files Available:
Native - ovr_Voip_GetPCMSize()
Unity - Platform.Voip.GetPCMSize()
Retrieve the current number of audio samples available to read from a specified user. This function is
inherently racy, it's possible that data can be added between a call to this function and a subsequent call to
ovr_Voip_GetPCM or ovr_Voip_GetPCMFloat.
Review the ovr_Voip_GetPCMSize page for information about the parameters and return data structure.
• Retrieve PCM Data with a Timestamp from a User (16 bit fixed point):
Native - ovr_Voip_GetPCMWithTimestamp()
Unity - Platform.Voip.GetPCMWithTimestamp()
Like ovr_Voip_GetPCM, this function copies available audio samples from a specified user
into a buffer. Along with the audio samples, this function also stores the timestamp of the first
sample in the output parameter timestamp. This timestamp can be used for synchronization, see
ovr_Voip_GetSyncTimestamp for more details. The data format is 16 bit fixed point 48khz mono.
This function may return data early, even if there's more data available, to keep the batch of audio samples
returned with a single timestamp small. For example, if 30ms worth of audio is in the buffer, this function
may return 480 samples (10ms) each time it's called. Therefore, it's recommended to call this as long as
there's data in the buffer (i.e. the function returns a non-zero result).
Review the ovr_Voip_GetPCMWithTimestamp page for information about the parameters and return data
structure.
• Retrieve PCM Data with a Timestamp from a User (32 bit floating point):
Native - ovr_Voip_GetPCMWithTimestampFloat()
Unity - Platform.Voip.GetPCMWithTimestampFloat()
Like ovr_Voip_GetPCMFloat, this function copies available audio samples from a specified user
into a buffer. Along with the audio samples, this function also stores the timestamp of the first
sample in the output parameter timestamp. This timestamp can be used for synchronization, see
ovr_Voip_GetSyncTimestamp for more details. The data format is 32 bit floating point 48khz mono.
This function may return data early, even if there's more data available, in order to keep the batch of audio
samples with a single timestamp small. For example, if there's 30ms worth of audio in the buffer, this
function may return 480 samples (10ms) each time it's called. Therefore, it's recommended to call this as
long as there's data in the buffer (i.e. the function returns a non-zero result).
Review the ovr_Voip_GetPCMWithTimestampFloat page for information about the parameters and return
data structure.
We do not recommend using the floating point methods unless necessary. This operation requires
additional resources when compared to the integer based process.
Oculus Platform | Voice Chat (VoIP) | 69
• Retrieve the Sync Timestamp for the Sender:
Native - ovr_Voip_GetSyncTimestamp()
Unity - Platform.Voip.GetSyncTimestamp()
Returns a timestamp used for synchronizing audio samples sent to the given user with an external data
stream.
Timestamps associated with audio frames are implicitly transmitted to remote peers; on the receiving side,
they can be obtained by using ovr_Voip_GetPCMWithTimestamp. ovr_Voip_GetSyncTimestamp is
used to fetch those timestamps on the sending side. An application can insert the value returned by this
function into each data packet and compare it to the value returned by GetPCMWithTimestamp on the
receiving side to determine the ordering of two events (sampling audio and composing a data packet).
Note: The timestamp is generated by an unspecified clock and won't represent wall-clock time. Use
ovr_Voip_GetSyncTimestampDifference to determine the difference between two timestamps
in microseconds.
This function assumes that a voice connection to the user already exists; it returns 0 if that isn't the case.
Review the ovr_Voip_GetSyncTimestamp page for information about the parameters and return data
structure.
• Retrieve the Difference (in µs) Between Two Timestamps:
Native - ovr_Voip_GetSyncTimestampDifference()
Unity - Platform.Voip.GetSyncTimestampDifference()
Retrieve the calculated difference between two sync timestamps, in microseconds.
returned by ovr_Voip_GetSyncTimestamp, ovr_Voip_GetPCMWithTimestamp, or
ovr_Voip_GetPCMWithTimestampFloat.
Return value will be negative if lhs is smaller than rhs, zero if both timestamps are the same, and positive
otherwise. The absolute value of the result is the time in microseconds between the two sync timestamps.
Review the ovr_Voip_GetSyncTimestampDifference page for information about the parameters and return
data structure.
• Retrieve the Mute State of the Microphone:
Native - ovr_Voip_GetSystemVoipMicrophoneMuted()
Unity - Platform.Voip.GetSystemVoipMicrophoneMuted()
Retrieves the mute state for the microphone. Review the ovr_Voip_GetSystemVoipMicrophoneMuted page
for information about the parameters and return data structure.
• Retrieve the VoIP System Status:
Native - ovr_Voip_GetSystemVoipStatus()
Unity - Platform.Voip.GetSystemVoipStatus()
Retrieve the VoIP system status. Information about the VoIP status can be found on SystemVoipStatus.
Review the ovr_Voip_GetSystemVoipStatus page for information about the parameters and return data
structure.
• Set the Microphone Filter Callback:
Native - ovr_Voip_SetMicrophoneFilterCallback()
Unity - Platform.Voip.SetMicrophoneFilterCallback()
70 | Voice Chat (VoIP) | Oculus Platform
Set a callback that will be called every time audio data is captured by the microphone. The callback function
must match this format:
void filterCallback(int16_t pcmData[], size_t pcmDataLength, int frequency, int numChannels);
The pcmData param is used for both input and output. pcmDataLength is the size of pcmData in
elements. numChannels will be 1 or 2. If numChannels is 2, then the channel data will be interleaved in
pcmData. frequency is the input data sample rate in hertz.
Review the ovr_Voip_SetMicrophoneFilterCallback page for information about the parameters and return
data structure.
• Mute/Unmute the Microphone:
Native - ovr_Voip_SetMicrophoneMuted()
Unity - Platform.Voip.SetMicrophoneMuted()
This function is used to enable or disable the local microphone. When muted, the microphone will not
transmit any audio. VoIP connections are unaffected by this state. New connections can be established or
closed whether the microphone is muted or not. This can be used to implement push-to-talk, or a local mute
button. The default state is unmuted.
Review the ovr_Voip_SetMicrophoneMuted page for information about the parameters and return data
structure.
• Set the VoIP Output Sample Rate:
Native - ovr_Voip_SetOutputSampleRate()
Unity - Platform.Voip.SetOutputSampleRate()
Sets the output sample rate. Audio data will be resampled as it is placed into the internal ringbuffer. Review
the ovr_Voip_SetOutputSampleRate page for information about the parameters and return data structure.
• Terminate the VoIP Connection:
Native - ovr_Voip_Stop()
Unity - Platform.Voip.Stop()
Ends a VoIP session with the specified user. Note that a muting functionality should be used to temporarily
stop sending audio; restarting a VoIP session after tearing it down may be an expensive operation.
Review the ovr_Voip_Stop page for information about the parameters and return data structure.
Implementation Overview - Unity
This section will walk you through the basic process of implementing VoIP in your Unity app. A complete
example of a Unity app The general process of implementing VoIP is:
1. Send the connection request. The user initiating the connection request calls Voip.Start with the userID
of the other user. Information about how to retrieve a userID can be found or the User and Friends on page
66.
2. Establish a callback as a notification handler to accept incoming connection requests by calling
Voip.Accept from the user receiving the request. For example, to accept all incoming connections, you
could use the following:
Voip.SetVoipConnectRequestCallback((Message<NetworkingPeer> msg) => {
Voip.Accept(msg.Data.ID);
});
Oculus Platform | Voice Chat (VoIP) | 71
3. Listen for the state change. Both users will be listening for the callback that the connection has been
established. Once this is received the users can begin exchanging data. An example of a state change
listener is:
Voip.SetVoipStateChangeCallback((Message<NetworkingPeer> msg) => {
Debug.LogFormat("peer {0} is in state {1}", msg.Data.ID, msg.Data.State);
});
4. Accept the connection request by calling Voip.Accept to open the connection.
5. (Optional) At any time, either user may call Voip.GetSystemVoipStatus to determine the state of a VoIP
connection with another user.
6. Add the playback component. The audio playback component handles buffering and playback of
voice data from a remote peer. You should add this component to a game object that represents a
connected user, such as their avatar. For each connected user, you’ll need to set the senderID field of
each VoipAudioSourceHiLevel object you create. This lets the system know which user's voice data
corresponds to each playback component. For example:
var audioSource = gameObject.AddComponent <VoipAudioSourceHiLevel>();
audioSource.senderID = remotePeer.ID;
7. You can access the Unity AudioSource object from the VoipAudioSourceHiLevel object to modify
playback options. For instance to spatialize the audio playback:
Var voipAudio = RemoteAvatar.GetComponent<VoipAudioSourceHiLevel>();
voipAudio.audioSource.spatialize = true;
8. To end a VoIP connection call:
Oculus.Platform.Voip.Stop(userID);
Voip.Stop(remotePeer.ID);
9. (Optional) At any time either user may mute their microphone by calling
Platform.Voip.SetMicrophoneMuted. Calling the same function will unmute the microphone. Call
Platform.Voip.GetSystemVoipMicrophoneMuted to check the mute state of the microphone.
Implementation Overview - Native
This section will walk you through the basic process of implementing VoIP in your Native app. The general
process of implementing VoIP is:
1. Send the connection request. The user initiating the connection request calls ovr_Voip_Start with the Id
of the user they would like to connect with. Information about retrieving user Id's can be found or the User
and Friends on page 66.
2. Accept the connection request. The user on the receiving end of the will be listening for notifications on
their message queue. Information about how the Platform SDK uses the message queue can be found
on the Requests and Messages page. After handling the request, the receiving user accepts by calling
ovr_Net_Accept with the peerID of the request.
3. Listen for the state change. Both users will be listening for the notification that the connection has been
established by listening for ovrMessage_Notification_Voip_StateChange. Once this is received the
users can begin exchanging data.
4. (Optional) At any time, either user may call ovr_Voip_GetSystemVoipStatus to determine the state of a
VoIP connection with another user.
5. Transmit PCM data. The connected microphone will begin capturing the voice data and the SDK will convert
to PCM data and transmit to the user the connection was established with.
6. Determine the size of the buffer. Call ovr_Voip_GetOutputBufferMaxSize to determine the max
output size, or the maximum number of elements that can be retrieved.
7. Retrieve PCM data for processing and playback. There are a number of methods you can use to
retrieve the PCM data depending on your application's use-case. Before you retrieve any data, call
72 | Voice Chat (VoIP) | Oculus Platform
ovr_Voip_GetPCMSize to determine the size of the data in the buffer. Then call one of the following
methods to retrieve the data. One of these methods needs to be called for each connection, PCM data will
only be returned for a single user connection.
a. ovr_Voip_GetPCM - Retrieves the PCM data in 16 bit fixed point 48khz mono format.
ovr_Voip_GetPCMWithTimestamp retrieves the same data with an additional timestamp that can be
used for synchronization.
b. ovr_Voip_GetPCMFloat - Retrieves the PCM data in 32 bit floating point 48khz mono.
ovr_Voip_GetPCMWithTimestampFloat retrieves the same data with an additional timestamp that
can be used for synchronization. Using floating point is not recommended unless necessary.
8. (Optional) After retrieving the PCM data, you may wish to spatialize the audio playback. Information about
spatializing audio can be found in the Audio SDK documentation.
9. (Optional) At any time either user may mute their microphone by calling
ovr_Voip_SetMicrophoneMuted. Calling the same function will unmute the microphone. Call
ovr_Voip_GetSystemVoipMicrophoneMuted to check the mute state of the microphone.
10.End the connection. Terminate the connection between users at any time, call ovr_Voip_Stop to close.
Notifications and the Message Queue
As mentioned earlier in this guide, the VoIP service uses notifications to update your app on the status of the
connection and service. Review the Requests and Messages page for more information about the message
queue and notifications.
For example, a native application may listen for state changes using:
case ovrMessage_Notification_Voip_StateChange: {
ovrNetworkingPeerHandle netPeer = ovr_Message_GetNetworkingPeer(message);
ovrPeerConnectionState netState = ovr_NetworkingPeer_GetState(netPeer);
printf(
"User: %llu, voip state %s\n",
ovr_NetworkingPeer_GetID(netPeer),
ovrPeerConnectionState_ToString(netState)
);
}
Break;
Example Implementation
The VrVoiceChat Unity app provided in the Platform SDK download demonstrates using the Cloud Storage
service to save multiple records and perform conflict resolution. Please see the Sample Apps page for more
information about the apps that are available.
using UnityEngine;
using System.Collections;
using Oculus.Platform;
using Oculus.Platform.Models;
// Helper class to manage the Voice-over-IP connection to the
// remote user
public class VoipManager
{
// the ID of the remote user I expect to talk to
private ulong m_remoteID;
// the last reported state of the VOIP connection
private PeerConnectionState m_state = PeerConnectionState.Unknown;
// the GameObject where the remote VOIP will project from
private readonly GameObject m_remoteHead;
public VoipManager(GameObject remoteHead)
{
m_remoteHead = remoteHead;
Voip.SetVoipConnectRequestCallback(VoipConnectRequestCallback);
Oculus Platform | Voice Chat (VoIP) | 73
}
Voip.SetVoipStateChangeCallback(VoipStateChangedCallback);
public void ConnectTo(ulong userID)
{
m_remoteID = userID;
var audioSource = m_remoteHead.AddComponent<VoipAudioSourceHiLevel>();
audioSource.senderID = userID;
}
// ID comparison is used to decide who initiates and who gets the Callback
if (PlatformManager.MyID < m_remoteID)
{
Voip.Start(userID);
}
public void Disconnect()
{
if (m_remoteID != 0)
{
Voip.Stop(m_remoteID);
Object.Destroy(m_remoteHead.GetComponent<VoipAudioSourceHiLevel>(), 0);
m_remoteID = 0;
m_state = PeerConnectionState.Unknown;
}
}
public bool Connected
{
get
{
return m_state == PeerConnectionState.Connected;
}
}
void VoipConnectRequestCallback(Message<NetworkingPeer> msg)
{
Debug.LogFormat("Voip request from {0}, authorized is {1}", msg.Data.ID, m_remoteID);
}
if (msg.Data.ID == m_remoteID)
{
Voip.Accept(msg.Data.ID);
}
void VoipStateChangedCallback(Message<NetworkingPeer> msg)
{
Debug.LogFormat("Voip state to {0} changed to {1}", msg.Data.ID, msg.Data.State);
if (msg.Data.ID == m_remoteID)
{
m_state = msg.Data.State;
}
}
}
if (m_state == PeerConnectionState.Timeout &&
// ID comparison is used to decide who initiates and who gets the Callback
PlatformManager.MyID < m_remoteID)
{
// keep trying until hangup!
Voip.Start(m_remoteID);
}
PlatformManager.SetBackgroundColorForState();
74 | User Verification | Oculus Platform
User Verification
User Verification allows you to verify the identity of each user accessing your application.
In addition to the basic Entitlement Check, this feature uses a client-provided nonce that can’t be tampered
with by the client that is provided to your trusted server and checked against the Oculus Platform. This user
verification does not replace the Entitlement Check performed in Initializing and Checking Entitlements.
Your application will call ovr_User_GetUserProof(), or Platform.User.GetUserProof() if you’re
using Unity, to retrieve the nonce. Then after passing to your server, make a S2S call to verify that the user is
who they claim to be.
Integrate User Verification
Minimal integration is required for User Verification, the only function you have to integrate is to retrieve the
nonce. The end-to-end flow for User Verification can be found in the diagram above.
Generate Nonce
Native - ovr_User_GetUserProof()
Unity - Platform.User.GetUserProof()
Review the ovr_User_GetUserProof page for information about the parameters and return data structure.
S2S REST Request
Certain actions require to you to interact directly with our server. See the Server-to-Server API Basics page for
information about interacting with our APIs.
Validate Nonce (POST)
Oculus Platform | User Verification | 75
Validate the nonce you retrieved from the client and verify that the nonce matches the User Id using a secure
S2S HTTP POST. Please see the User and Friends page for more information on retrieving the User Id.
$ curl -d "access_token=$APP_ACCESSTOKEN" -d "nonce=$NONCE" -d “user_id=$USER_ID”
https://graph.oculus.com/user_nonce_validate
The request returns a verification of the nonce. For example:
{"is_valid":true}
© Copyright 2026 Paperzz