ReconnectAndReJoin when you're the last player in the room

Hello developers,
I'm making a game supporting drop-in drop-out multiplayer, where the state for the rooms is changed by RPCs and stored in instantiated scene objects. Now most of the time this works great, if a player drops and reconnects then the master client can resend the state when they rejoin - the problem is when the very last player disconnects (e.g. due to timeout) and then on reconnection receives the PhotonNetwork.Instantiate made objects in their original state, overriding the current ones in the scene.

Am I just approaching this the wrong way? Most tutorials and documentation recommend storing state on instantiated objects (and using observed components with OnPhotonSerializeView functions), so I went this way as it seems most natural with the API.

I don't need to support reconnecting to a totally player-less game, only reconnecting due to timeout when still in the scene room.

It seems to me that the standard behaviour for a reconnecting player should be to just carry on with the instantiated objects they own, rather than delete them, re-instantiate them, then require someone else to fill the data. This would fix the "last player reconnect" case, but also wouldn't harm the rest of the logic.

Without such a 'keep alive my own objects on disconnect' option (and I don't mean autocleanup = false, as the network instantiates replace the objects anyway), then my only options seem to be:
1) Move ALL state to room properties (a total refactor, and probably less efficient)
2) Move ALL state to un-instantiated scene objects (again a total refactor, as would still need boiler plate code to sync up visual objects to centralised state)
3) Give up on reconnecting and just re-create a new room after regaining connectivity - maybe the simplest option but annoying as I use photon room events for playfab integration.

I realise this is kind-of a rage-post, but this is a big blocker at the moment, and I wanted to know what the Photon Community would suggest. Surely someone else has hit this issue?

Thank you for your time,
Duncan Stead

Comments

  • JohnTube
    JohnTube ✭✭✭✭✭
    edited January 2018
    Hi @Dunk,

    Thank you for choosing Photon!

    One valuable member of our community (@robertsze) described a similar situation here.

    I think you could add a fourth option:

    4) Find a way to support this use case in PUN.

    With help of @jeanfabre and @Christian_Simon probably :)
  • Hi John, thanks for the reply.

    OK so it sounds like there's a way to workaround the issue:
    "If this is very important for you, you may start looking into the code to skip "some" events when rejoined, for instance, events where sender actor number is equal to the actor number of the local player."

    ...but there's nothing in the thread that explains how this might be done (it gets resolved via email). I'm guessing I would need to change some of Photon's code to achieve this? It would be useful if the e-mail exchange was available somewhere...

    Duncan
  • Hi,

    can you check this:

    http://forum.photonengine.com/discussion/8550/photon-not-disconnecting-if-we-disconnect-internet-while-connected#latest

    With the trick mentionned in that thread, you can detect very early that a connection was lost and you can therefore, disconnect and reconnect + rejoin as a result, achieving your reconnection routine.

    If you combine this with Playerttl and RoomTtl, you can achieve a seamless experience for other players in the room, as the player that got disconnected will not leave the room but only be unavailable, while the client goes through the process of reconnecting.

    Let me know if that's not suitable.

    Bye,

    Jean
  • @jeanfabre I already have reconnect + rejoin working when a disconnect is detected. The trick you linked to would possibly detect a disconnected state earlier, but the problem is the reconnection event itself, and the fact that the buffered PhotonNetwork.Instantiate RPCs will overwrite my own objects, even when I'm the only player left in the room.
    Possibly you mean to detect that a disconnect is *going* to happen, and manually make a copy of my objects, wait for Photon to overwrite them, then place my copy back over the top :wink: ?

    I'm currently thinking that I shouldn't rely on PhotonNetwork.Instantiate, and instead use my own RPCs for object creation so I can handle this 'last player disconnect' data overwrite case.

    Though now I think about it, I'm not sure even that is a great approach, because if the last master-client times out, keeps it's objects around, then a new player joins and becomes the master client, has no information about the state of the game, so resets the scene owned objects, and the original master client reconnects - they will get sent a copy of the reset objects by the new master anyway... I'd have to make sure that players can't connect during a last-master reconnect somehow...

    Oh well, maybe Photon 2 will be better at handling this stuff :)
  • JohnTube
    JohnTube ✭✭✭✭✭
    edited January 2018
    Though now I think about it, I'm not sure even that is a great approach, because if the last master-client times out, keeps it's objects around, then a new player joins and becomes the master client, has no information about the state of the game, so resets the scene owned objects, and the original master client reconnects - they will get sent a copy of the reset objects by the new master anyway... I'd have to make sure that players can't connect during a last-master reconnect somehow...


    Yes, I think the reason behind the issue is the following:

    1. PUN assumes that there is always a (master) client present.
    2. PUN assumes that as soon as the room is empty it should no longer exist (EmptyRoomTTL = 0).
    3. PUN seems to hold some room state on (master) client(s) so once the room is empty part of the state is lost. e.g. PUN caches (buffer) instantiation calls but somehow does not seem to cache all updates to those instantiated objects or update the instantiation event(s) with the object(s)' new state(s).

    Some new Photon Realtime/LoadBalancing concepts that are part of PUN also do not play well with PUN 100% like the rejoin and the pseudo async features.

    @Dunk
    If this use case is a must and you want to allow joining/rejoining an empty room then you need to dive into the code and together we can make PUN 2 better yes!
  • Hi,

    sorry, I think I misunderstood your problem.

    Maybe indeed raising EmptyRoomTtl and keeping track of your data in the room custom properties would guarantee persistence when the last player needs reconnecting. Or maybe increase the PlayerTtl. Have you tried these tricks?

    Photon 2 will behave the same on these features.

    Bye,

    Jean

  • My room time to live and player time to live are both at 60 seconds yes.
    Using custom properties to store room state was nuclear option 1) in my first post.

    I'm leaning towards centralising state in un-instantiated scene objects, as this would give me most flexibility for other types of game that contain a lot of state.
    Since I'm using PlayFab for my backend, I could even rewrite it to do all the game-logic inside cloudscript, and not use photon at all. I really don't want to do that though, because javascript :worried:
  • Hi,

    I am still at lost to really grasp your case though. if your player reconnect within its PlayerTtl, it hasn't lost anything, do we agree on that or is not what you experience to begin with? your reconnection attempt never takes 60 seconds right? to the last player who was the masterClient rejoin the same room where he was inactive and regain the masterClient privilege, and all the network scene objects are in place as it was before he lost connection, there should be no action required during that rejoin to fall back into the state things where when the last player got disconnected.

    I am trying to rephrase your case so that I can make sense of it, so bear with me :) and let me know if that's not what you mean.

    Bye,

    Jean
  • Yes you're right, the last player who was the masterClient *should* be able to rejoin the room and resume the game with their scene objects intact as they have all the information.

    Unfortunately, photon will delete ALL PhotonNetwork.instantiated objects upon a temporary disconnect, and then try to rebuild them on reconnect (presumably from the system's buffered PhotonNetwork.Instantiate RPC and instantiationData).

    That's my situation :)

    Thanks for your attention on this, I'm glad someone cares!

    Duncan
  • Hi,

    ok, there is a trick that when a network object is instantiated, we know the first data is wrong because it's the initial data, so you ignore that and then the last position ( or what ever data synchronized) will be called after and that's only when you do take it into account.

    Have you tried that? you can find an implementation in PhotonViewTransform.cs check the variable m_firstTake and how it's used.

    Let me know if that's not suitable, but it's likely the right path for you.

    Bye,

    Jean
  • I don't want new objects to be instantiated *at all* during a master-client reconnection. I had a look at PhotonTransformView.cs and the m_firstTake variable seems to be used for initialising member data differently when reading the first stream as to consequent streams. I have similar logic myself in my own class's OnPhotonSerializeView implementation for a different purpose, so I know what that's for. Unfortunately, that stage is already too late, as the original objects have been destroyed, and photon has already spawned new ones.
  • Hi,

    Then you should instead maybe have proxies, that are only bound to network objects programmatically ( via unique Ids that you could list in the room properties or player player properties, and so when a player is out and pending for reconnection, only the proxies gets destroyed and reconstructed following player leaving and rentering the room. when rentering, the proxy check for what Id it needs to find in the room, if it finds it, take it as is regain control but do not touch it's data, else it creates a new one from scratch.

    Maybe PUN structure is indeed not suitable for your case? Have you tried to use the Unity SDK, it uses Photon and you simply raise events, nothing more, you can then deal with your network objects how you see fit.


    Bye,

    Jean
  • Manually handling the object lifetimes may indeed be the way to go here. I already manually send RPC's to fill objects with their correct state, so I could send a manual 'instantiation' RPC before that one.

    Something similar to the example in the documentation:
    https://doc.photonengine.com/en-us/pun/current/gameplay/instantiation

    Thanks,
    Duncan