Remote Avatar created by master cannot be controlled by remote user

Options
BFX
BFX
Scenario: We have two users A and B
  • A connects to the room (becoming master)
  • A configures its local avatar (which is already there, doesn't get instantiated because of specific requirements of this project). Specifically, it assigns to the already existing avatar in the scene a random Photon View ID between 10 and 999 (not using AllocateViewID)
  • A sends a custom event to .Others containing it's Photon View ID and other information (event caching is active)
  • B connects to the room (becoming guest)
  • B configures its local avatar
  • B sends a custom event to .Others containing it's Photon View ID and other information
  • B receives the (cached) event sent by A and use it to instantiate the remote avatar, extract the PhotonView component and change the ID to the one in the event custom data (connecting it to A)
  • B receives the event sent by B and use it to instantiate the remote avatar, extract the PhotonView component and change the ID to the one in the event custom data (connecting it to B)

Expected result: Both A and B remote avatars work fine
Actual Result: Only A's remote Avatar on B works

In other words:
- A can see the remote avatar of B getting created but the remote avatar doesn't move
- B can see the remote avatar of A getting created and moving correctly
Reversing the order of connection (B as Master and A as Guest) we obtain the exact same result.


Running this in the editor I have noticed that when A is master the remote avatar of B has the "Controlled Locally: (Master)" checked, which makes me think that since A creates the remote avatar as master, automatically it becomes the owner. However the owner of the avatar is still the scene, which is strange.
When working correctly (A as guest and B as master) I can see the remote avatar of B has "Controlled locally" unchecked.

I don't get it, it should be an ownership problem but the remote avatar owner is still the scene.

Please check the following screenshots:

Remote Avatar not Working (Created by A as Master)
https://ibb.co/vJB6Kvg

Remote Avatar Working (Created by A as Guest)
https://ibb.co/kqrCJgC

When I send the custom event I use:
RaiseEventOptions raiseEventOptions = new RaiseEventOptions {
                CachingOption = EventCaching.AddToRoomCache,
                Receivers = ReceiverGroup.Others
            };
            SendOptions sendOptions = new SendOptions {
                Reliability = true
            };

            PhotonNetwork.RaiseEvent (eventCode, content, raiseEventOptions, sendOptions);


UPDATE
I have also tried to request Ownership on B before and after sending the custom event to A which triggers the creation of B's remote avatar, but it doesn't work:

Called on B:
localPC_Photonview.RequestOwnership();

Should be executed by A:
public void OnOwnershipRequest (PhotonView targetView, Player requestingPlayer) {
             Debug.Log ("OnOwnershipRequest(): Player " + requestingPlayer + " requests ownership of: " + targetView + ".");
            targetView.TransferOwnership (requestingPlayer);
         }
The photonView in the remote prefab is set to "Request"


UPDATE 2
We are trying to force a transfer ownership on A (Master) after receiving the custom event from B (Guest). The idea is to send over in the custom event data B's player ID. Since it must be serialized I expect that to be an Int or a string. However. in B we can only find PhotonNetwork.LocalPlayer.UserId which is a long string and the TransferOwnership method seems accepting only an int based on your documentation:
◆ TransferOwnership() [1/2]
void TransferOwnership ( int newOwnerId )
Transfers the ownership of this PhotonView (and GameObject) to another player.

The owner/controller of a PhotonView is also the client which sends position updates of the GameObject.

Are we going to the right direction?


UPDATE 3
Another experiment: B (Guest) sends in the custom event the actorNumber (PhotonNewtork.Player.ActorNumber) and A(Master) use it to transfer the ownership:
GameObject remotePC = Instantiate (Resources.Load ("RemotePlayerController"), avatarInfo.player_box_pos, avatarInfo.player_box_rot) as GameObject;
            PhotonView remotePC_Photonview = remotePC.GetComponent<PhotonView> (); 
            if (remotePC_Photonview != null) { 
                remotePC_Photonview.ViewID = avatarInfo.player_box_ID;
                remotePC_Photonview.TransferOwnership(avatarInfo.player_id);
                Debug.LogFormat ("[Multiplayer.cs] Connected Remote Avatar Box [RemotePC_PhotonView:{0},position:{1},owner:{2}]", remotePC_Photonview.ViewID, RemoteSpawnPos,avatarInfo.player_id);
            } else {
                Debug.LogError ("[Multiplayer.cs] Cannot access Remote Avatar PhotoView");
                return;
            }

Still nothing, although running A(Master) in the Editor we can see a change: The Locally Controlled checkbox is finally unchecked by the word master is still appearing and B's remote Avatar doesn't work

https://ibb.co/gJ8sK7n

LOOK LIKE THIS "CONTROLLED LOCALLY: (MASTER) is the main problem

Clearly we are making some basic mistakes here....

Answers

  • BFX
    Options
    UPDATE 4
    The problem still persist anyway I found out that, since the local avatar is already in the scene, the PhotonViewID was already set so there was no need of assigning another one.
    1. I have removed the lines forcing the change of the PhotonView ID --> It doesn't work
    2. I have added PhotonNetwork.RegisterPhotonView(View) with and without overwriting the ViewID ---> it doesn't work
    3. I have tried to instantiate the remote avatars at runtime and then use PhotonNetwork.AllocateViewID --> unfortunately my VR rig doesn't work in that case so it's hard to test but I can confirm the remote avatar created on the editor does still have "Controlled Locally: (Master)"
  • BFX
    BFX
    edited June 2020
    Options
    Update 5
    Getting close to a final solution but still with some problem.
    We have Set all the PhotonView in the avatars (local and remote) to "REQUEST"

    On the local client (since we need to use pre-instantiated prefab we cannot use AllocatePhotonViewID):
    localPC_Photonview.ViewID = UnityEngine.Random.Range (1000, 1999); 
    PhotonNetwork.RegisterPhotonView(localPC_Photonview);
    localPC_Photonview.RequestOwnership();
    

    then we send PhotonViewID and ActorNumber to the other clients using a custom event. When the client receive that they execute:
    remotePC_Photonview.ViewID = avatarInfo.player_box_ID;
                    remotePC_Photonview.TransferOwnership(avatarInfo.player_id);
    

    And now both local and remote avatar work on Master and guest (Editor and Oculus Quest, I can exchange roles and it keep on working)

    BUT
    I have two errors now

    Error number 1
    When one client moves the other gets multiple instances of this error
    IndexOutOfRangeException: Index was outside the bounds of the array.
    Photon.Pun.PhotonStream.ReceiveNext () (at Assets/Photon/PhotonUnityNetworking/Code/PunClasses.cs:694)
    Photon.Pun.PhotonTransformView.OnPhotonSerializeView (Photon.Pun.PhotonStream stream, Photon.Pun.PhotonMessageInfo info) (at Assets/Photon/PhotonUnityNetworking/Code/Views/PhotonTransformView.cs:112)
    Photon.Pun.PhotonView.DeserializeComponent (UnityEngine.Component component, Photon.Pun.PhotonStream stream, Photon.Pun.PhotonMessageInfo info) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonView.cs:351)
    Photon.Pun.PhotonView.DeserializeView (Photon.Pun.PhotonStream stream, Photon.Pun.PhotonMessageInfo info) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonView.cs:341)
    Photon.Pun.PhotonNetwork.OnSerializeRead (System.Object[] data, Photon.Realtime.Player sender, System.Int32 networkTime, System.Int16 correctPrefix) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:1732)
    Photon.Pun.PhotonNetwork.OnEvent (ExitGames.Client.Photon.EventData photonEvent) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:2102)
    Photon.Realtime.LoadBalancingClient.OnEvent (ExitGames.Client.Photon.EventData photonEvent) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:3018)
    ExitGames.Client.Photon.PeerBase.DeserializeMessageAndCallback (ExitGames.Client.Photon.StreamBuffer stream) (at <11e9abbca912444aa80ed58a280369fc>:0)
    ExitGames.Client.Photon.EnetPeer.DispatchIncomingCommands () (at <11e9abbca912444aa80ed58a280369fc>:0)
    ExitGames.Client.Photon.PhotonPeer.DispatchIncomingCommands () (at <11e9abbca912444aa80ed58a280369fc>:0)
    Photon.Pun.PhotonHandler.Dispatch () (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:205)
    Photon.Pun.PhotonHandler.FixedUpdate () (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:139)
    

    Error number 2
    When the Editor disconnect, the Oculus Quest gets stuck and we think this is due to the fact the ownership of the remote client gets passed to the oculus quest. Is there any way to remove the remote avatar when the client disconnect? I have tried to find the
    PhotonNetwork.autoCleanUpPlayerObjects
    
    but it seems it has been removed.
    Not totally sure if that is the problem affecting the Quest, but I would like to exclude it just to be sure and also, as general approach, the avatar of a disconnected user should disappear.
    Another approach would be using public override void OnPlayerLeftRoom (Player other) and find all the views associated to a player to destroy them before they gets re-assigned to the master but not sure how to do it...
  • jeanfabre
    Options
    Hi,

    it's generally a bad idea to temper with viewIDs and instantiation manually, It will be a lot more solid and mainstreamed to use some layer of indirections and always have a regular networked object that is instantiated by Pun itself. You can use IPunPoolPrefab interface if you want to use some pooling strategy ( check DefaultPool class and how it's used for an example on how to work with this.

    so if you want to instantiate a non networked object when a player join, make a regular network object that in turn will manage a regular object that you instantiate with unity. You have one layer of indirection, your base network object controls the actual avatar instance and it can then control it anyway it wants, yet you haven't created an overly complex custom networking instantiation system.

    To check on such design pattern, check the basic tutorial and how the HUD UI prefab is used on each network object and how it's handled for cases like player leaving, and joining, it's a straight forward.

    https://doc.photonengine.com/en-us/pun/v2/demos-and-tutorials/pun-basics-tutorial/player-ui-prefab

    Let me know if you have more questions.

    Bye,

    Jean
  • BFX
    Options
    Hello, thanks a lot for your answer. Can you please elaborate this with an example?
    so if you want to instantiate a non networked object when a player join, make a regular network object that in turn will manage a regular object that you instantiate with unity. You have one layer of indirection, your base network object controls the actual avatar instance and it can then control it anyway it wants, yet you haven't created an overly complex custom networking instantiation system.

    If with "Create a network object" you are referring to use PhotonNetwork.Instantiate() unfortunately I canon use it as the local avatar need to be already instantiate in the scene (The oculus VR rig doesn't work when added at runtime).

    My approach is to extract the PhotonView from the existing avatar, replacing the ViewID with a random number (AllocateViewID() wouldn't work since the view is already allocated), send the custom event, receive the event on the other client, instantiate the removeAvatar prefab (which contains a PhotonView) connect it with the ID and then let it synchronise. This approach is reported in the custom instantiation described in the Photon documentation and it works for me.

    The problem I'm experiencing is that:
    - A (Desktop) connects to the room and become Master
    - B (Oculus) connects to the room and become guest
    - B's remote avatar is created on A
    - A-s remote avatar is created on B
    - Everything works well till A disconnects ---> A gets stuck for some reason

    If I'm understanding correctly you're suggesting to create network objects on the Local player using PhotonNetwork.instantiate, get a reference to the object on the remote client using OnPhotonInstantiate(PhotonMessageInfo info) and then connect that to remote avatar?
  • BFX
    Options
    Overall, the major problem is that Local and remote avatar prefab are not the same ones (because the Oculus avatar doesn't work on desktop) and also the local avatar doesn't work if allocated at runtime.
    Not sure how to create a network object and let it pilot my avatar since I need the PhotonTransformView to be directly connected to the head and hands of my VR Character.
    The only solution for me is to add a PhotonView & TransformView at runtime which doesn't work.
  • Tobias
    Options
    > The only solution for me is to add a PhotonView & TransformView at runtime which doesn't work.

    You can do that but have to register them in the ObservedComponents list of the PhotonView.
    Make sure that receiving the updates for that PhotonView will handle the change (with and without those components).

    > Everything works well till A disconnects ---> A gets stuck for some reason

    How does that look? What is happening?


    > If I'm understanding correctly you're suggesting to create network objects on the Local player using PhotonNetwork.instantiate, get a reference to the object on the remote client using OnPhotonInstantiate(PhotonMessageInfo info) and then connect that to remote avatar?

    Jean is on vacation but I think the idea is more:
    When you need to control any objects that are not network instantiated and already exist, you can still do a network instantiate and "bind" the regular object to the networked one (which is created as a placeholder / wrapper).
    You basically have a blank networked object and can communicate with/through that. Then tell everyone which character / objects it will control.

    > since I need the PhotonTransformView to be directly connected to the head and hands of my VR Character.

    As long as you can find hands and head in the hierarchy of the object, you can add those or just access the values from a script in a higher level.
  • BFX
    Options
    At the end the only way to make it work was to create PhotonView, connect PhotonTransformView adding it to ObservedComponents list of the PhotonView and registering the view.
    Not sure why using the PhotonViewID created by PhotonView when added as component to an object already present in the current scene wouldn't work.