Oculus Avatar SDK with PUN

Options
Hello everyone,

We're testing out Photon for our new multiplayer Oculus application.

I followed the tutorial on Photon's website "Using the Oculus Avatar SDK" here: https://doc.photonengine.com/en-us/pun/current/tutorials/oculusavatarsdk

I followed every step one by one, but I'm having a strange problem.

The first player that creates & joins the room has no errors, but the second player gives out a NullReferenceException on the PhotonAvatarView.cs attached to the LocalAvatar object. Here's the script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

public class PhotonAvatarView : MonoBehaviour {

    private PhotonView photonView;
    private OvrAvatar ovrAvatar;
    private OvrAvatarRemoteDriver remoteDriver;

    private List<byte[]> packetData;

    public void Awake()
    {
        photonView = GetComponent<PhotonView>();
        if (photonView.isMine)
        {
            ovrAvatar = GetComponent<OvrAvatar>();

            packetData = new List<byte[]>();
        }
        else
        {
            remoteDriver = GetComponent<OvrAvatarRemoteDriver>();
        }
    }

    public void OnEnable()
    {
        if (photonView.isMine)
        {
            ovrAvatar.RecordPackets = true;
            ovrAvatar.PacketRecorded += OnLocalAvatarPacketRecorded;
        }
    }

    public void OnDisable()
    {
        if (photonView.isMine)
        {
            ovrAvatar.RecordPackets = false;
            ovrAvatar.PacketRecorded -= OnLocalAvatarPacketRecorded;
        }
    }

    private int localSequence;

    public void OnLocalAvatarPacketRecorded(object sender, OvrAvatar.PacketEventArgs args)
    {
        using (MemoryStream outputStream = new MemoryStream())
        {
            BinaryWriter writer = new BinaryWriter(outputStream);

            var size = Oculus.Avatar.CAPI.ovrAvatarPacket_GetSize(args.Packet.ovrNativePacket);
            byte[] data = new byte[size];
            Oculus.Avatar.CAPI.ovrAvatarPacket_Write(args.Packet.ovrNativePacket, size, data);

            writer.Write(localSequence++);
            writer.Write(size);
            writer.Write(data);

            packetData.Add(outputStream.ToArray());
        }
    }

    private void DeserializeAndQueuePacketData(byte[] data)
    {
        using (MemoryStream inputStream = new MemoryStream(data))
        {
            BinaryReader reader = new BinaryReader(inputStream);
            int remoteSequence = reader.ReadInt32();

            int size = reader.ReadInt32();
            byte[] sdkData = reader.ReadBytes(size);

            System.IntPtr packet = Oculus.Avatar.CAPI.ovrAvatarPacket_Read((System.UInt32)data.Length, sdkData);
            remoteDriver.QueuePacket(remoteSequence, new OvrAvatarPacket { ovrNativePacket = packet });
        }
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            if (packetData.Count == 0)
            {
                return;
            }

            stream.SendNext(packetData.Count);

            foreach (byte[] b in packetData)
            {
                stream.SendNext(b);
            }

            packetData.Clear();
        }

        if (stream.isReading)
        {
            int num = (int)stream.ReceiveNext();

            for (int counter = 0; counter < num; ++counter)
            {
                byte[] data = (byte[])stream.ReceiveNext();

                DeserializeAndQueuePacketData(data);
            }
        }
    }
}
It gives the error on line:
if (packetData.Count == 0)

saying:
NullReferenceException: Object reference not set to an instance of an object PhotonAvatarView.OnPhotonSerializeView (.PhotonStream stream, PhotonMessageInfo info) (at Assets/_PhantasyVR/Scripts/PhotonAvatarView.cs:88) System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222) Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation. System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:232) System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MethodBase.cs:115) PhotonView.ExecuteComponentOnSerialize (UnityEngine.Component component, .PhotonStream stream, PhotonMessageInfo info) (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/PhotonView.cs:553) PhotonView.SerializeComponent (UnityEngine.Component component, .PhotonStream stream, PhotonMessageInfo info) (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/PhotonView.cs:457) PhotonView.SerializeView (.PhotonStream stream, PhotonMessageInfo info) (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/PhotonView.cs:352) NetworkingPeer.OnSerializeWrite (.PhotonView view) (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs:4217) NetworkingPeer.RunViewUpdate () (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/NetworkingPeer.cs:4079) PhotonHandler.Update () (at Assets/Photon Unity Networking/Plugins/PhotonNetwork/PhotonHandler.cs:164)

This results in the second player not being able to send packets, therefore while the second player is the one having the errors, it can see both players' head movement correctly, while the first player can't receive the headset movement of the second player.

Any help would be much appreciated.

Comments

  • mertcanb
    Options
    SOLVED:

    Upon testing millions of variations, I figured that the execution order prevents the object from actually registering the creator of itself before it assigns ovrAvatar in the PhotonAvatarView. So the order actually goes as follows:

    1) Instantiate Game Object (in OnJoinedRoom of NetworkManager)
    2) PhotonAvatarView Awake
    3) PhotonAvatarView OnEnable (not really sure about the order of 2&3)
    4) GameObject finally registers its owner

    So when I took the code in Awake + OnEnable and put it in Start, it started working like a charm.

    So the working code is:

    using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; public class PhotonAvatarView : MonoBehaviour { private PhotonView photonView; private OvrAvatar ovrAvatar; private OvrAvatarRemoteDriver remoteDriver; private List<byte[]> packetData; public void Start() { photonView = GetComponent<PhotonView>(); if (photonView.isMine) { ovrAvatar = GetComponent<OvrAvatar>(); packetData = new List<byte[]>(); ovrAvatar.RecordPackets = true; ovrAvatar.PacketRecorded += OnLocalAvatarPacketRecorded; } else { remoteDriver = GetComponent<OvrAvatarRemoteDriver>(); } } public void OnDisable() { if (photonView.isMine) { ovrAvatar.RecordPackets = false; ovrAvatar.PacketRecorded -= OnLocalAvatarPacketRecorded; } } private int localSequence; public void OnLocalAvatarPacketRecorded(object sender, OvrAvatar.PacketEventArgs args) { using (MemoryStream outputStream = new MemoryStream()) { BinaryWriter writer = new BinaryWriter(outputStream); var size = Oculus.Avatar.CAPI.ovrAvatarPacket_GetSize(args.Packet.ovrNativePacket); byte[] data = new byte[size]; Oculus.Avatar.CAPI.ovrAvatarPacket_Write(args.Packet.ovrNativePacket, size, data); writer.Write(localSequence++); writer.Write(size); writer.Write(data); packetData.Add(outputStream.ToArray()); } } private void DeserializeAndQueuePacketData(byte[] data) { using (MemoryStream inputStream = new MemoryStream(data)) { BinaryReader reader = new BinaryReader(inputStream); int remoteSequence = reader.ReadInt32(); int size = reader.ReadInt32(); byte[] sdkData = reader.ReadBytes(size); System.IntPtr packet = Oculus.Avatar.CAPI.ovrAvatarPacket_Read((System.UInt32)data.Length, sdkData); remoteDriver.QueuePacket(remoteSequence, new OvrAvatarPacket { ovrNativePacket = packet }); } } private void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { if (stream.isWriting) { if (packetData.Count == 0) { return; } stream.SendNext(packetData.Count); foreach (byte[] b in packetData) { stream.SendNext(b); } packetData.Clear(); } if (stream.isReading) { int num = (int)stream.ReceiveNext(); for (int counter = 0; counter < num; ++counter) { byte[] data = (byte[])stream.ReceiveNext(); DeserializeAndQueuePacketData(data); } } } }
  • OMG thank you for figuring that out, I just got to this point and it was really starting to get annoying.