Using photon to make recordings.

I have a multiplayer VR unity application that uses photon (PUN) (mostly to keep all game objects synchronized). I now wish to add a recording element to the VR application, the bulk of the recording consists of changes to game object positions. But these changes are already being sent by photon to synchronize all the clients! So I tried to implement my recording system using photon.

The recording has to be done server side because players can connect/disconnect as they please.

Possible approaches:
1) Attach an extra server sided application to the photon room that listens for changes made by the clients.
This seems the most logical approach, the problem is that since my clients use PUN my recording client that listens would also have to use PUN and thus unity (as far as I understand photon).
Using unity would create a lot of overhead, since I would have to run a unity application for each multiplayer VR session.

2) Create a private photon server and listen to traffic.
This one is a bit out there because I don't think that there is a "supported" way to do this, so this would have to be done in a hacky way + I'm taking a big leap in assuming that I can even deserialize the data I need.

3) Don't use photon and send data a different way.
This seems needlessly complex (a photon connection already exists, so why not use it?), data will be sent twice, .... Doesn't seem like a good approach.

Is there any better way that I'm missing?
Any questions/answers or suggestions are appreciated.

Best Answer

  • JohnTube
    JohnTube ✭✭✭✭✭
    edited July 2021 Answer ✓
    You can extract this information from reading the PUN client code.
    Sometimes you can find some comments with hints:
    but be careful, those could be outdated or incomplete
    example
    /// RPC Hashtable Structure
    /// (byte)0 -> (int) ViewId (combined from actorNr and actor-unique-id)
    /// (byte)1 -> (short) prefix (level)
    /// (byte)2 -> (int) server timestamp
    /// (byte)3 -> (string) methodname
    /// (byte)4 -> (object[]) parameters
    /// (byte)5 -> (byte) method shortcut (alternative to name)
    ///
    /// This is sent as event (code: 200) which will contain a sender (origin of this RPC).

    You need to parse and inspect each PUN event data structure.
    Document it internally and implement it in the plugin.
    Also don't forget you need to register PUN's custom types...from the plugin also.

Answers

  • hi, @ReplayMaker

    Second approach is quite double. you can use self hosted version and write your own plugin that will collect RaiseEvent operation request and take info from there. the only question is serialization deserialization that can be taken from PUN if I'm right.

    best,
    ilya
  • chvetsov wrote: »
    hi, @ReplayMaker

    Second approach is quite double. you can use self hosted version and write your own plugin that will collect RaiseEvent operation request and take info from there. the only question is serialization deserialization that can be taken from PUN if I'm right.

    best,
    ilya

    Thanks, I'm trying your suggested route and I am able to intercept the data! After looking around, I'm suspecting the useful data is contained in IRaiseEventCallInfo.Request.
    But I'm also having some trouble deserializing :/.

    What I'm assuming I'm supposed to do:
    - Find out some way what the ev codes mean. Most traffic has an ev code that is 200, 202 or 0 (which I have read somewhere is a special case).
    - Deserialize based on the ev code

    You are right that I might be able to use ExitGames.Client.Photon.Protocol to deserialize/serialize some data but I don't think this helps for everything, for example: My project uses PhotonView to synchronize game objects, so I have no idea how or what data it is sending. (I'm assuming I can use ExitGames.Client.Photon.Protocol without Unity)

    I'm hoping there is some easy way to find out what ev codes mean? Or just to deserialize in general?

    Docs where I found most information:
    https://doc.photonengine.com/en-US/realtime/current/reference/serialization-in-photon
    https://doc-api.photonengine.com/en/plugins/current/interface_photon_1_1_hive_1_1_plugin_1_1_i_raise_event_request.html
    https://doc.photonengine.com/en-US/pun/v1/gameplay/rpcsandraiseevent/
  • JohnTube
    JohnTube ✭✭✭✭✭

    hi @ReplayMaker,

    Thank you for choosing Photon!

    You need to extract PUN events yourself.
    See here.
    You may copy paste some PUN client code to your custom plugin.

  • Thanks @JohnTube.

    This does bring a lot more clarity.

    Looking at Photon.Pun.PhotonNetwork.cs#OnEvent(EventData photonEvent):
    Event code 0 is not catched in the switch and seems to be ignored, but this can't be right? Looking at the traffic I am intercepting most of the synchronizing data is sent using event code 0.

    Looking at the documentation I was only able to find this page:
    https://doc.photonengine.com/en-us/realtime/current/gameplay/cached-events

    Which says that "An event code equal to 0 is a wild card for event codes.".

    How/Where are events with event code 0 handled?
  • JohnTube
    JohnTube ✭✭✭✭✭
    Hi @ReplayMaker,
    but this can't be right?

    It is right.

    You already found out it's about controlling room events cache on the server so other clients sometimes won't get this event and won't have to handle it.

    If you don't explicitly call RaiseEvent yourself in your custom user code with event code set to 0 then you should check other info.Request properties most important one should be info.Request.Cache which maps to raiseEventOption.CachingOption (enum of type Photon.Realtime.EventCaching). In the plugin the values should be found using Photon.Hive.Plugin.CacheOperations. Most likely the value is either 6 RemoveFromRoomCache or 7 RemoveFromRoomCacheForActorsLeft.

    You can find out more by inspecting event payload data: info.Request.Data.

    In any case you can skip events with code 0?
    Looking at the traffic I am intercepting most of the synchronizing data is sent using event code 0.
    I don't think so maybe it appears so in your tests but in real scenario it shouldn't be the case.
    Unless you are manually calling RaiseEvent(code=0) a lot and forgot about this.

    In any case you can find all calls to RaiseEvent in the client code.
  • JohnTube wrote: »
    Hi @ReplayMaker,
    but this can't be right?

    It is right.

    You already found out it's about controlling room events cache on the server so other clients sometimes won't get this event and won't have to handle it.

    If you don't explicitly call RaiseEvent yourself in your custom user code with event code set to 0 then you should check other info.Request properties most important one should be info.Request.Cache which maps to raiseEventOption.CachingOption (enum of type Photon.Realtime.EventCaching). In the plugin the values should be found using Photon.Hive.Plugin.CacheOperations. Most likely the value is either 6 RemoveFromRoomCache or 7 RemoveFromRoomCacheForActorsLeft.

    You can find out more by inspecting event payload data: info.Request.Data.

    In any case you can skip events with code 0?
    Looking at the traffic I am intercepting most of the synchronizing data is sent using event code 0.
    I don't think so maybe it appears so in your tests but in real scenario it shouldn't be the case.
    Unless you are manually calling RaiseEvent(code=0) a lot and forgot about this.

    In any case you can find all calls to RaiseEvent in the client code.

    Thanks a lot, I am now able to deserialize the photon view data. You were right event code 0 did not contain the information, event code 1 contains the information.

    Data provided by a photonview 'packet':
    1) int - ?
    2) int - some sort of gameobject id
    3) vector3 - position gameobject
    4) quaternion - rotation gameobject
    5) vector3 - scale gameobject
    6) vector3 - ?
    7) quaternion - ?
    8) vector3 - ?

    Out of curiosity what are the question marks?
  • JohnTube
    JohnTube ✭✭✭✭✭
    edited July 2021 Answer ✓
    You can extract this information from reading the PUN client code.
    Sometimes you can find some comments with hints:
    but be careful, those could be outdated or incomplete
    example
    /// RPC Hashtable Structure
    /// (byte)0 -> (int) ViewId (combined from actorNr and actor-unique-id)
    /// (byte)1 -> (short) prefix (level)
    /// (byte)2 -> (int) server timestamp
    /// (byte)3 -> (string) methodname
    /// (byte)4 -> (object[]) parameters
    /// (byte)5 -> (byte) method shortcut (alternative to name)
    ///
    /// This is sent as event (code: 200) which will contain a sender (origin of this RPC).

    You need to parse and inspect each PUN event data structure.
    Document it internally and implement it in the plugin.
    Also don't forget you need to register PUN's custom types...from the plugin also.
  • Ok sounds good. And yes I implemented the custom types :smile:.

    I bypassed the whole type-registering part and directly used the deserialization functions which might not be the 'correct' way to do it. But it works fine and is all I need for now, thanks!