Best way for PUN Players to change prefab

Vlaxep

Active member
I've been working on this problem for weeks now, thought I had it a few times but I keep discovering new issues.

I need the player to be able to switch to a designated prefab when they press a button when inside a trigger.

The greatest problem I'm facing is that in a PUN 2 situation, simply calling a PhotonNetwork.Destroy on the old one and a PhotonNetwork.Instantiate on for the new one does not keep everything in sync over the network. Locally it works great!

I have even tried this by updating the PhotonViewID and Owner+Controller to be the same as it was before, but the result is that other players witness the Destroy and the Instantiate, but nothing more. Everything beyond that no longer syncs - transform, movement... - the new character remains in place where it instantiated regardless of what the player does.

I'm looking for the simplest way to achieve this, and have looked in to:

1. Piggybacking the Respawn system, though this seems to only return the current prefab to it's original state, I can't find a way to modify and respawn with a different prefab.

2. SpawnObjectsOnDeath - though this doesn't improve my above problem of syncs

3. Using SpawnManagerBase to Spawn a new character for the Local Player, though the script is very heavy and I'm looking for guidance on which parts I need to utilise in a new function to make sure this is properly broadcast across network, and caters for future joining players ie. buffered data.

4. Somehow transferring all component required over to NewObject, OR adding/replacing components on the current object? My main concern here is that the skeleton is different, I'm not sure how it would work. Inventory/Item Sets etc. will also be different.

I'm quite happy to manually update each and every property/variable whenever this occurs, I'm just not sure which need to be done to achieve synch when this new prefab arrives in to the scene.

Any guidance on the 'cleanest' way to achieve this would be fantastic!
 
I haven't done this before but at a high level you'll need to combine something like this thread:


With changing the owner as well. In this situation you should not use the Respawner or spawning any objects upon death.
 
Hi @Justin , that thread doesn't delve in to the Opsive+ViewID and assosciation between 'Player' and View ID.

I can successfully instantiate a NewPrefab for a remote Player at their call, which they send via [PunRPC] to the Master. The Master then instantiates the NewPrefab and sets the owner to the Actor who called for a NewPrefab. This works to a point - the Master AND the Remote client see the NewPrefab instantiated correctly.

However from this point the Network sync is no longer actively updating for that character, pretty sure this is due to whatever SpawnManagerBase's reference to the 'Player's Object's ViewID' being different from the 'Object's ViewID' that he is now controlling.

So to be clear, from the remoteClient's perspective, he has no problems, he controls the newPrefab as normal.

From the MasterClient's perspective, he sees the NewPrefab remain where it was instantiated, nothing is updated from that point on, though it shows that the remotePlayer owns it.

So how can I update this Opsive reference to which thing all the PunAnimatorSync, PunTransformSync etc. need so they can be sending the correct information?

Here is my code, https://pastebin.com/RviBFZCJ
please note:
- The initial call that begins the operation is line 109
'SpawnBigCharacter(Player player, int PViewID, int OWNER, Vector3 POSN, Quaternion ROTN)

- I created a new RaiseEvent ID using the 'playerInstantiation' one as a template, one from line 407:
else if (photonEvent.Code == PhotonEventIDs.PlayerPrefabSwitch)
 
So for example My first remoteClient joins and is ViewID '2002'

He requests a prefab change, gets it from Master and the Master runs the code which create a new allocated View id of '2003', the sequential next for that owner I think that's how that works.

I tested and when the remoteClient performs any actions which update with RPC I get very tellale error (which is good! Can see what's going wrong for sure now!)

Received RPC "OnItemAbilityActiveRPC" for viewID 2002 but this PhotonView does not exist! Was remote PV. Owner called. By: #02 'Player 0176' Maybe GO was destroyed but RPC not cleaned up.

So I know this for sure now. Problem is I have tried setting the new Object's PhotonViewID to the same '2002' as it was before but I get duplicate PhotonView error and code stops in it's tracks.
 
Also have tried to update the Opsive parameter for PhotonView via this:

AddPhotonView(photonView);

But I get the error:

ArgumentException: An item with the same key has already been added. Key: 2
System.Collections.Generic.Dictionary`2[TKey,TValue].TryInsert (TKey key, TValue value, System.Collections.Generic.InsertionBehavior behavior) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Collections.Generic.Dictionary`2[TKey,TValue].Add (TKey key, TValue value) (at <437ba245d8404784b9fbab9b439ac908>:0)
Opsive.UltimateCharacterController.AddOns.Multiplayer.PhotonPun.Game.SpawnManagerBase.AddPhotonView (Photon.Pun.PhotonView photonView) (at Assets/Opsive/UltimateCharacterController/Add-Ons/Multiplayer/PhotonPUN/Scripts/Game/SpawnManagerBase.cs:295)
Opsive.UltimateCharacterController.AddOns.Multiplayer.PhotonPun.Game.SpawnManagerBase.SpawnBigCharacter (Photon.Realtime.Player player, System.Int32 PViewID, System.Int32 OWNER, UnityEngine.Vector3 POSN, UnityEngine.Quaternion ROTN) (at Assets/Opsive/UltimateCharacterController/Add-Ons/Multiplayer/PhotonPUN/Scripts/Game/SpawnManagerBase.cs:131)
Opsive.UltimateCharacterController.AddOns.Multiplayer.PhotonPun.Game.SingleCharacterSpawnManager.SwitchBigPrefab (Photon.Realtime.Player player, System.Int32 PViewID, System.Int32 OWNER, UnityEngine.Vector3 POSN, UnityEngine.Quaternion ROTN) (at Assets/Opsive/UltimateCharacterController/Add-Ons/Multiplayer/PhotonPUN/Scripts/Game/SingleCharacterSpawnManager.cs:44)
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <437ba245d8404784b9fbab9b439ac908>:0)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <437ba245d8404784b9fbab9b439ac908>:0)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <437ba245d8404784b9fbab9b439ac908>:0)
Photon.Pun.PhotonNetwork.ExecuteRpc (ExitGames.Client.Photon.Hashtable rpcData, Photon.Realtime.Player sender) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:540)
Photon.Pun.PhotonNetwork.OnEvent (ExitGames.Client.Photon.EventData photonEvent) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:2158)
Photon.Realtime.LoadBalancingClient.OnEvent (ExitGames.Client.Photon.EventData photonEvent) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:3157)
ExitGames.Client.Photon.PeerBase.DeserializeMessageAndCallback (ExitGames.Client.Photon.StreamBuffer stream) (at <7dfc8d6157ef4d379354c19104de3140>:0)
ExitGames.Client.Photon.EnetPeer.DispatchIncomingCommands () (at <7dfc8d6157ef4d379354c19104de3140>:0)
ExitGames.Client.Photon.PhotonPeer.DispatchIncomingCommands () (at <7dfc8d6157ef4d379354c19104de3140>:0)
Photon.Pun.PhotonHandler.Dispatch () (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:208)
Photon.Pun.PhotonHandler.FixedUpdate () (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:142)
 
Ah I don't think I was using AddPhotonView(photonView); correctly at all.

What I'm looking for is one of two things:

1. A way to avoid the duplicate ViewID error, which I don't know why PhotonNetwork.Destroy doesn't get rid of it so I can free it up to Set as the ViewID for the NewPrefab's PhotonView.ViewID

2. Possibly easier, I'm searching for the value that Opsive Scripts use to determine which ViewID to send to for synching. Can't tell yet whether it's the 'm_Players' array, or the 'm_ActorNumberByPhotonViewIndex' dictionary, or something in one of the PUN scripts on the PUNCharacter......

The following error shows me clearly that the remoteRPC's are being received by the MasterClient, but are being directed to ViewID 2002, whereas I can't avoid having the newPrefab ViewID go to the next successive ViewID ('2003') as the 'duplicate error' breaks the code otherwise.

Received RPC "OnItemAbilityActiveRPC" for viewID 2002 but this PhotonView does not exist! Was remote PV. Owner called. By: #02 'Player 0962' Maybe GO was destroyed but RPC not cleaned up.
UnityEngine.Debug:LogWarning(Object)
Photon.Pun.PhotonNetwork:ExecuteRpc(Hashtable, Player) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:398)
Photon.Pun.PhotonNetwork:OnEvent(EventData) (at Assets/Photon/PhotonUnityNetworking/Code/PhotonNetworkPart.cs:2158)

Sooooo If there is a way to say "Now this Player is controlling viewID'XXXX' and please send all Opsive transform/animator sync information there", I think that should be fine right?
 
Last edited:
Unfortunately you're getting to the level where it is hard for me to understand what is going on. When you do find a solution let me know if you have to change anything on the pun integration side of things to make it easier for you to update.
 
Unfortunately you're getting to the level where it is hard for me to understand what is going on. When you do find a solution let me know if you have to change anything on the pun integration side of things to make it easier for you to update.

@Justin I will. But if it does simplify anything all of the above can be ignored and all I'm searching for is:

How do all the Opsive CharacterPunSync components target their RPC's? Because when assigning ownership and ownerinternal to the actor, all of his OpsivePunSync components are still directing RPC update traffic to the old/destroyed view, which doesn't exist. If I can simply update this target, my problems should be over!
 
All of the RPCs are sent from the PhotonView on the GameObject. Take a look at PunCharacter for the complete script but here's an example:

Code:
            photonView.RPC("OnAbilityActiveRPC", RpcTarget.Others, ability.Index, active);
 
Top