Singletons, DontDestroyOnLoad, and "PhotonView ID duplicate found"

Options
I wanted to ask about the error message that seems to already have a lot of posts and questions about it. The error message in PUN 2 comes from PhotonNetworkPart.cs:

"PhotonView ID duplicate found: {0}. New: {1} old: {2}. Maybe one wasn't destroyed on scene load?! Check for 'DontDestroyOnLoad'. Destroying old entry, adding new."

I understand that scene objects have a pre-determined ViewID and obviously that multiple objects can't have the same ViewID. However, I don't understand why the default behavior is to destroy the old object and keep the new one, considering it's the opposite of what you want from an object with DontDestroyOnLoad called on it (usually done on singletons in Unity, which do the destruction of newly created additional instances of singetons).

Most of the answer's I've been seeing say to just not use PhotonViews on singletons, but that doesn't make sense. From reading the code in PhotonNetworkPart.cs, there's nothing that obviously suggests that it would cause issues if the old object was kept, and this post suggests that you could even change it to do that.

I do wish that this behavior defaulted to deleting the new object instead of the old one, or at least was configurable. Refactoring code to not have PhotonViews on DontDestroyOnLoad objects is a potentially massive amount of work for no real reason. Like the poster above did, I'd rather just rewrite the source code instead and spare myself the effort.

Comments

  • JohnTube
    JohnTube ✭✭✭✭✭
    Options
    Hi @Saruto,

    Thank you for choosing Photon!

    By design, by default, network objects instantiated using PhotonNetwork.Instantiate* methods are automatically cleaned up when you leave the room.
    This lifecycle is a guarantee that we do not have clashes between View IDs and that PhotonView features (RPCs & serialization) work as expected.

    Now before we try to think about more advanced or complex options, could you tell us about your use case.
    Maybe there is another way to do it more PUN friendly.

    The question is what keeps the network objects instantiated using PhotonNetwork.Instantiate* alive between rooms or between scenes? and why do you that?
  • Saruto
    Options
    Hi @JohnTube, thanks for responding!

    My specific use case is the singleton pattern and how it's usually implemented in Unity. Specifically this: I have a GameObject in the scene with a script attached to it. The barebones version of the script looks like this:
    public class MySingletonType : MonoBehaviour {
        public static MySingletonType Instance;
    
        void Awake() {
            if (Instance == null) {
                Instance = this;
                DontDestroyOnLoad(gameObject);
            } else {
                Destroy(gameObject);
            }
        }
    }
    

    So the first instance of MySingletonType in the scene will be stored in a public static variable called Instance, and the GameObject is set to persist between scenes. Any further instances of this object that are created are destroyed upon seeing that there already exists an instance of the class. The main advantage of singletons is that they provide a conveniently accessible location for run-time storage of objects, player state, and game state that persists between scenes of the game.

    I would like to mention that I'm not instantiating my singleton with an Instantiate or PhotonNetwork.Instantiate call. My singleton has a PhotonView that's owned by the Scene - it's a part of my game's Character Select scene. From there, my game transitions into the Gameplay scene (and the singleton persists just fine). But then I eventually transition back into the Character Select scene. Now I run into this situation, where I have the old singleton object coming in from the last scene, and a new singleton object that's inherently a part of the Character Select scene. With the above code snippet, this will work out fine - the new singleton object simply destroys itself.

    However, because it has a PhotonView component, instead it'll destroy the old singleton object, which in this case should persist. For my use case, I need the singleton to have RPCs on it, in order to coordinate and synchronize some aspects of the game state (eg. information about the other players and their current state, notification when they die, etc).

    I could maybe get rid of this singleton and replace it with non-persistant objects, but it would have massive implications and require re-writing large portions of the game. If it's something I *had* to do because there's no other alternatives in Photon then I would understand, but it doesn't seem like having clashing ViewIDs is actually that big of an issue, since you can just destroy one of them and then everything's fine (do correct me if I'm wrong).

    If it's not a big issue to simply delete one of the objects with a clashing ViewID, I would prefer that Photon would provide a mechanism that lets me choose which object I would like to keep when this clash occurs and abort the RegisterPhotonView / PhotonView's Awake function gracefully when it occurs rather than reporting a Debug.LogError, but maybe it's more complicated/not possible to do that?

    Again, thanks for the response.