PhotonPeer.Service() causing NullReferenceException

Options
JohnTube
JohnTube ✭✭✭✭✭
The official and unofficial Photon comments I've read about PhotonPeer.Service() all suggest that it should be refactored ! Here's some examples of what's told about it :
...easy but ineffective. should be refined to using dispatch every frame and send outgoing on demand.

Other than the occasional TimeoutDisconnect, Unity Editor "quickly loses" reference to the loadBalancingClient (it becomes null). I don't know why this is happening exactly...but since I'm calling Service() in Monobehavior's Update() I had to add an explicit check to test if the loadBalancingClient is not null to avoid an infinite number of NullReferenceException.

I want to know how to proceed to make my own replacement of Service(). Should I use Coroutines ? one for incoming and one for outgoing traffic ?

Comments

  • Tobias
    Options
    Using a Coroutine is a nice idea. It should be a simple solution for timing the calls nicely.

    In our demos, I usually implement DispatchIncomingCommands in a while-loop in Update() (until it returns false) and then check if the Environment.TickCount is higher than my value for "next Send".

    I think that explicit checks for LoadBalancingClient == null are best practice. Alternatively, you might want to make sure it can't be null ever. I don't know which scripts set it to null in your case. Usually, you should be able to keep one client all the time, even while it's not connected.
  • JohnTube
    JohnTube ✭✭✭✭✭
    Options
    For the null check I implemented it in a Property getter, so it should be "fixed"

    The Coroutine way is not that obvious, I started an implementation that didn't seem to work properly so I went back to using the default built-in Service in Update for now. I need to do more investigation about DispatchIncomingCommands and SendOutgoingCommands and SendAcksOnly.

    I didn't get the Environment.TickCount test thought.

    I also wanted to mention that since Application.runInBackground does not work with Android and I did not want to implement a background Android Service in the native Android plugin (I don't really know if that's feasible with PhotonPeer.Service) I'm re connecting to Photon every time the app is unpaused (OnApplicationPause(false)) and Rejoin a room if needed.
  • Tobias
    Options
    When the app comes back to foreground, it will automatically attempt to use the connection and only if the timeout time passed, it will disconnect you.
    So you should rely on the disconnect of the client and only re-connect when it's really needed (because you got a timeout / disconnect).
    If you don't want to do that, you can also disconnect when going into background. Simply call Disconnect accrodingly. But this will drop you out of a game any time the app is not focused for whatever reason and no matter how briefly.
  • JohnTube
    JohnTube ✭✭✭✭✭
    Options
    Tobias, Application.runInBackground is important in Photon (to keep the connection with the server and to receive incoming events) but it does not work on Android.

    So Photon automatically disconnects while in background with reason : DisconnectedByServer ! And I think this is also related to this topic.

    But I was not asking about this specifically, I just need to know how to re-implement LoadBalancingPeer.Service() properly ! I still did not get that Environment.TickCount test.
  • Tobias
    Options
    While the app is in the background, it does nothing. This includes the timeout disconnect (not happening while in background but when you're back).
    If the app is in the background for too long, it will timeout when it gets back but not before it gets CPU time again. I still need to figure out what's causing the issue in the topic you liked. But that's another topic.

    You can use Environment.TickCount to check the systems time for cheap but not fully correct timing. It's not tick accurate, even though the name implies that.

    Anyways. The idea is that you use it when you want to do things in a fixed frequency.
    Initially, you store the TickCount when you connect or start the app. In your update loop, you can now substract that initial TickCount from the new/current TickCount. The difference is how much time passed. When at least 50 or 100 ms passed, you can execute SendOutgoingCommands() and re-set the "initial" TickCount.

    Alternatively use a Stopwatch. Start it, check elapsed milliseconds and when enough time passed, then you can execute your method and reset and restart the timer.

    You could call DispatchIncomingCommands() in every update you do. Even multiple times, until it returns false:
    while (DispatchIncomingCommands()) {}
    Aside from the high frequency dispatch, you want to a) aggregate your state to send it to the others and b) actually send whatever needs to be sent to the server and others.
    So you do a) in ~100ms frequency and b) maybe every 50ms.