cancel
Showing results for 
Search instead for 
Did you mean: 

Share spatial anchors example doesn't work

Shavier
Protege

I'm trying to use the Share spatial anchors provided in the oculus repository:
https://github.com/oculus-samples/Unity-SharedSpatialAnchors 

But when I start the app and enter the Share Spatial Anchors scene, it shows on the log window: "You are not authenticated to use this app. Shared Spatial Anchors will not work."

Reading the code it seems that the application is not getting the oculus user id correctly, but everyone is saying on forums that the project works, so I don't know if im missing something related to the user on the quest 2 or something...

1 ACCEPTED SOLUTION

Accepted Solutions

I think you also need to config the Data Use Checkup in the developer manager - and at least Request access for UserID.

View solution in original post

20 REPLIES 20

TriplePete
Protege

I think you have to create the application in the Meta Quest developer manager. You need to access the Platform Services to make it work.

Yep I've been trying that, setting the app as an alpha version and giving me test user access, and download the app from the applab instead of installing it through unity, but still getting the same message.
For some reason the OculusID that the user gets is empty and the user ID is 0.
(And yes, I set the AppID on the Oculus platform settings...) So I dont know what else can I do 😞

I think you also need to config the Data Use Checkup in the developer manager - and at least Request access for UserID.

You saved my life. That was it, I had to add the Used ID and User profile on the Data Use Checkup and then it worked! Thank you 😍

yarwad
Explorer

I'm having difficulties too.  Does anyone know if two headsets that have the same userID can use the shared spatial anchors example?  Not sure if this is the problem but I have multiple headsets but only one Meta profile.

Nope, you cant use the same meta profile. Even if you try to open the application with the same meta profile it should tell you that you cant open the application in multiple device. Here is a tutorial on the forum i posted to help people getting the share spatial anchors to work: 

https://communityforums.atmeta.com/t5/Unity-VR-Development/Spatial-Anchors-issues-and-quot-tutorial-... 

Awesome, yeah I created a second account and got it to work.  

Thanks on the tutorial btw, it's super helpful.

While I got you... I'm trying to disambiguate shared spatial anchors from Photon because I want to use another networking solution. 

From what you see, does it seem like Photon is handling any of the core shared anchor functionality across the network.  Or is it just doing room management and syncing transforms, and the Oculus Integration handling all the shared anchor stuff?

You can use the network solution you want, in my case im using Mirror network, (in case you are using a client -server model) you just have to initialize the oculus sdk on code like this:
Variables to declare in the class:

// The size of the packet we are sending and receiving
    private const int UuidSize = 16;

    // Reusable buffer to serialize the data into
    private byte[] _sendUuidBuffer = new byte[1];
    private byte[] _getUuidBuffer = new byte[UuidSize];
    private byte[] _fakePacket = new byte[1];
    private string _oculusUsername;
    private ulong _oculusUserId;
    private Guid _fakeUuid;

    private const string UserIdsKey = "userids";
    private const char Separator = ',';
    private const byte PacketFormat = 0;

Oculus Initialization:

Array.Resize(ref _fakePacket, 1 + UuidSize);
                _fakePacket[0] = PacketFormat;

                var offset = 1;
                var fakeBytes = new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };

                _fakeUuid = new Guid(fakeBytes);
                PackUuid(_fakeUuid, _fakePacket, ref offset);

                Core.Initialize();
                Users.GetLoggedInUser().OnComplete(GetLoggedInUserCallback);

 

The GetLoggedInUserCallback function goes like:

 private void GetLoggedInUserCallback(Message msg)
    {
        if (msg.IsError)
        {
            Log.Instance.LogMessage("GetLoggedInUserCallback: failed with error: " + msg.GetError());
            return;
        }

        Log.Instance.LogMessage("GetLoggedInUserCallback: success with message: " + msg + " type: " + msg.Type);

        var isLoggedInUserMessage = msg.Type == Message.MessageType.User_GetLoggedInUser;

        if (!isLoggedInUserMessage)
        {
            return;
        }

        _oculusUsername = msg.GetUser().OculusID;
        _oculusUserId = msg.GetUser().ID;

        Log.Instance.LogMessage("Info del usuario");
        Log.Instance.LogMessage(msg.GetUser().ToString());

        Log.Instance.LogMessage("GetLoggedInUserCallback: oculus user name: " + _oculusUsername + " oculus id: " + _oculusUserId);

        if (_oculusUserId == 0) {
            Log.Instance.LogMessage("You are not authenticated to use this app. Shared Spatial Anchors will not work.");
        }
            
        //This is a function send to the server by the client [Command] attribute in Mirror
        SendOculusIDToServer("" + _oculusUserId);

    }

In the SendOculusIDToServer you send the id from the client to the server and store it in some array. Be sure you are doing this on a Player object (in my case you can only send functions to the server in player objects, not sure if its the same in other network solutions.

Then when you put the share anchor prefab on scene, make sure you save ir locally and then get the users array in the server so you can share the spatial anchors to those users. For that, use the OnShaerButtonPressed function in the anchor.
I modified a little bit the function and goes like this:

public void OnShareButtonPressed()
    {
       
        if (!IsReadyToShare())
        {
            return;
        }

        IsSelectedForShare = true;

        OVRSpatialAnchor.SaveOptions saveOptions;
        saveOptions.Storage = OVRSpace.StorageLocation.Cloud;
        _spatialAnchor.Save(saveOptions, (spatialAnchor, isSuccessful) =>
        {            
            if (isSuccessful)
            {
                //The server manager is where i store the array with the oculus ids from the players, and the GetUserList returns a List<string> with those ids
                var userIds = ServerManager.Instance.GetUserList();
                ICollection<OVRSpaceUser> spaceUserList = new List<OVRSpaceUser>();
                foreach (string strUsername in userIds)
                {
                    spaceUserList.Add(new OVRSpaceUser(ulong.Parse(strUsername)));
                }

                OVRSpatialAnchor.Share(new List<OVRSpatialAnchor> { spatialAnchor }, spaceUserList, OnShareComplete);

            }
            else
            {
                Log.Instance.LogMessage("The cloud save failed");               
            }
        });
    }

Theeeenn, on the OnShareComplete, you have to send the result to the clients:

private static void OnShareComplete(ICollection<OVRSpatialAnchor> spatialAnchors, OVRSpatialAnchor.OperationResult result)
    {
        if (result != OVRSpatialAnchor.OperationResult.Success)
        {
            return;
        }

        var uuids = new Guid[spatialAnchors.Count];
        var uuidIndex = 0;

        foreach (var spatialAnchor in spatialAnchors)
        {
            uuids[uuidIndex] = spatialAnchor.Uuid;
            ++uuidIndex;
        }
        //This calls the PLAYER object and tell it to send the anchors info to the server, so it can send it to other players
        ClientConectionManager.Instance.localPlayer.SendAnchorsToServer(uuids, (uint)uuids.Length, true);
    }

 

Almost done. Here are the last parts, The player class get the order, send the info to the server, and the server kind of configure the anchors with that info, Then makes a RPC call (send the info to the clients)

public void SenAnchorsToServer(Guid[] uuids, uint numUuids, bool isBuffered)
    {
        PublishAnchorUuids(uuids, numUuids, isBuffered);
    }

    [Command]
    public void PublishAnchorUuids(Guid[] uuids, uint numUuids, bool isBuffered)
    {

        Array.Resize(ref _sendUuidBuffer, 1 + UuidSize * (int)numUuids);
        _sendUuidBuffer[0] = PacketFormat;

        var offset = 1;
        for (var i = 0; i < numUuids; i++)
        {
            PackUuid(uuids[i], _sendUuidBuffer, ref offset);
        }


        CheckForAnchorsShared(_sendUuidBuffer);
    }

    private static void PackUuid(Guid uuid, byte[] buf, ref int offset)
    {
        Buffer.BlockCopy(uuid.ToByteArray(), 0, buf, offset, UuidSize);
        offset += 16;
    }

    [ClientRpc]
    private void CheckForAnchorsShared(byte[] uuidsPacket)
    {

        if (isLocalPlayer)
        {
            return;
        }

        var isInvalidPacketSize = uuidsPacket.Length % UuidSize != 1;

        if (isInvalidPacketSize)
        {
            Log.Instance.LogMessage($"{nameof(CheckForAnchorsShared)}: invalid packet size: {uuidsPacket.Length} should be 1+{UuidSize}*numUuidsShared");
            return;
        }

        var isInvalidPacketType = uuidsPacket[0] != PacketFormat;

        if (isInvalidPacketType)
        {
            Log.Instance.LogMessage(nameof(CheckForAnchorsShared) + " : invalid packet type: " + uuidsPacket.Length);
            return;
        }

        var numUuidsShared = (uuidsPacket.Length - 1) / UuidSize;
        var isEmptyUuids = numUuidsShared == 0;

        if (isEmptyUuids)
        {
            Log.Instance.LogMessage(nameof(CheckForAnchorsShared) + " : we received a no-op packet");
            return;
        }

        Log.Instance.LogMessage(nameof(CheckForAnchorsShared) + " : we received a valid uuid packet");

        var uuids = new HashSet<Guid>();
        var offset = 1;

        for (var i = 0; i < numUuidsShared; i++)
        {
            // We need to copy exactly 16 bytes here because Guid() expects a byte buffer sized to exactly 16 bytes

            Buffer.BlockCopy(uuidsPacket, offset, _getUuidBuffer, 0, UuidSize);
            offset += UuidSize;

            var uuid = new Guid(_getUuidBuffer);

            var shouldExit = uuid == _fakeUuid;

            if (shouldExit)
            {
                Log.Instance.LogMessage(nameof(CheckForAnchorsShared) + " : received the fakeUuid/noop... exiting");
                return;
            }

            uuids.Add(uuid);
        }

        Debug.Log(nameof(CheckForAnchorsShared) + " : set of uuids shared: " + uuids.Count);
        SharedAnchorLoader.Instance.LoadAnchorsFromRemote(uuids);


    }

 

Annndd i think thats all about how to make the sharing in other network solution.
I also save the anchor locally when it is created on the clients (not the one that create it, but the ones that load it from the cloud) so i dont have to share the anchors again. 

 

Hope it helps you, let me know if you have some problem ^^.

Muchuts
Honored Guest

Awesome.  Thanks so much!  I'll give it a shot and see what happens.  Cheers!