Authoritative Server, Card Game, How to store game state/objects and sync individual actions

Options
Using unity 2017 and Photon On Premise server and have a question about how to store/sync data with the players.

I previously completed a single player version of my game in Unity. I am now building a multiplayer mode.

I setup Photon Server on my machine, using the load balancer/master/game server config. My unity game is now able to connect to the master server and then to the game server and send requests. All of this is functioning properly.

My game data is pretty simple. Basically a deck of cards per player separated into stacks, with a few shared stacks.
Game
	SharedStack1
		Card10
	SharedStack2
		Card11
	Players
		Player1
			Stack1
				Card1
			Stack2
				Card2
				Card3
		Player2
			Stack3
				Card4
			Stack4
				Card5
				Card6
What I'd like to do initialize all the decks/stacks on the server, and when match making is complete and the game is ready to start, send an event that contains all the cards for each player (Each client would get the other players cards as well, so they can watch them play visually). In unity, the client would receive this event , store a local gamestate, and then visually build it.

Then, as each player moves cards from stack to stack, their move would be validated client side, then submitted to the server and processed. If valid, it would then be sent to the other players so the move can be made locally on their side and then reflected in their local version of the game state.

What I want to do, is send each "action" individually, as opposed to sending the entire GameProperties hash table to each user everytime something changes. In the case that a user tries to make a move that shows as valid on client, but invalid on server, I would trigger a "OutOfSync" event for that client and when recieved, unity would freeze their game and reinitialize the gamestate/visuals.

I'm unsure of what the best way to send these actions are, or if i am going down the wrong road and should just use the game properties the way the documentation suggests.

Comments

  • chvetsov
    Options
    hi, @tudda
    i would recommend you to use plugins. please take a look at documentation here
    https://doc.photonengine.com/en-us/onpremise/current/plugins/manual
    and here
    https://doc.photonengine.com/en-us/onpremise/current/plugins/plugins-faq/

    best,
    ilya
  • tudda
    Options
    I have reviewed it and am working on writing my plugin now.

    I guess the core of my question is,

    1) if I store a custom object in game properties, and update it everytime a player makes a move, will the entire custom object be repeatedly sent back out to the users? (I do not want this, it would be a waste of bandwidth)

    2) Since I do want to send it sometimes, I need to serialize it. I am reading through this:
    https://doc.photonengine.com/en-us/realtime/current/reference/serialization-in-photon#photon_supported_types

    and it says that I need to write serialize routines for my custom class. However, my custom class is really just LIsts of a class, which contains standard types and enums inheritting from byte.

    Is there no other way to handle this besides writing serialization methods for each custom class involved?
  • tudda
    Options
    In my custom plugin I have:
    
            public override bool SetupInstance(IPluginHost host, Dictionary<string, string> config, out string errorMsg)
            {
                bool registered  = ExitGames.Client.Photon.PhotonPeer.RegisterType(typeof(NertzGameState), (byte)'N', SerializeFunction, DeserializeFunction);
                return base.SetupInstance(host, config, out errorMsg);
            }
            
            private object DeserializeFunction(byte[] bytes)
            {
                return NertzGameState.Deserialize(bytes);
            }
    
            private byte[] SerializeFunction(object o)
            {
                return ((NertzGameState)o).Serialize();
            }
    I've stepped through the code to ensure that TryRegisterType is being hit, and is returning true.

    Now, in my plugin, "OnCreateGame", I am calling:

    PluginHost.SetProperties(actorNr: 0, properties: new Hashtable() { { "NertzGameState", nertzGameState } }, expected: null, broadcast: false);


    This is causing the following error to be sent back to my client:

    3:57:21 PM : ----OperationResponse; Operation Code: 227; Return Code: 32752; Debug Message: System.IO.InvalidDataException: cannot serialize(): Nertz.Shared.NertzGameState
    at Photon.SocketServer.Rpc.Protocols.GpBinaryByte.GpBinaryByteWriter.Write(IBinaryWriter writer, Object value, Boolean setType, CustomTypeCache privateCustomTypeCache, Int32 depth) in h:\svncontent\photon-socketserver-sdk_cloud\src\Photon.SocketServer\Rpc\Protocols\GpBinaryByte\GpBinaryByteWriter.cs:line 413


    I put breakpoints on my serialize/deserialize routines and it's not even hitting them. I'm unsure what I am missing here?

  • tudda
    Options
    I resolved my problem with serialization. For anyone who is having trouble with on-premise server and the serializer not processing your custom type, you should double check the method you used to register the custom type. It seems that different methods are used with different products.

    The real time documentation shows
    bool testRegistered = PhotonPeer.RegisterType(typeof(TestSerializeClass), (byte)'T', TestSerializeFunction, TestDeserializeFunction);

    The plugin Documentation shows
    bool testRegistered = host.TryRegisterType(typeof(TestSerializeClass), (byte) 'N', TestSerializeFunction, TestDeserializeFunction);

    and the on Premise documentation shows:
    bool testRegistered = Photon.SocketServer.Protocol.TryRegisterCustomType(typeof(TestSerializeClass), (byte)'T', TestSerializeFunction, TestDeserializeFunction);

    The last example worked for me. Until i used that version, it was registering it, but when the event would from my OutgoingMasterServerPeer, that instance of the PeerBase did not have the type registered.

    Just figured i'd try to save someone a headache
  • chvetsov
    Options
    hi, @tudda

    it is true that you should use diffrent ways to register custom type for our different products. Way you chose is not correct for plugins.


    best,
    ilya
  • JohnTube
    JohnTube ✭✭✭✭✭
    edited September 2017
    Options
    Hi @tudda,

    Thank you for choosing Photon and sorry for the inconvenience!

    I also want to save others the headache by updating the docs.

    So you are saying, from a plugin:

    this works:
    bool testRegistered = Photon.SocketServer.Protocol.TryRegisterCustomType(typeof(TestSerializeClass), (byte)'T', TestSerializeFunction, TestDeserializeFunction);
    this does not work:
    bool testRegistered = host.TryRegisterType(typeof(TestSerializeClass), (byte) 'N', TestSerializeFunction, TestDeserializeFunction);

    Is that it?

    and @chvetsov do you confirm this is the expected behaviour?
  • tudda
    tudda
    edited September 2017
    Options
    In "on premise server", the documentation for Serialization says to use

    bool testRegistered = Photon.SocketServer.Protocol.TryRegisterCustomType(typeof(TestSerializeClass), (byte)'T', TestSerializeFunction, TestDeserializeFunction);

    This is correct and worked properly.

    However, I was also implementing a plugin at the same time, so I was reviewing documentation here:
    https://doc.photonengine.com/en-us/onpremise/current/plugins/manual

    and that has this code:

    bool testRegistered = host.TryRegisterType(typeof(TestSerializeClass), (byte) 'N', TestSerializeFunction, TestDeserializeFunction);

    So if you are implementing a plugin in your on-premise server, it's not entirely clear which one you should use, and it appears to work when the line of the code executes, so you don't really know why it's not being found.

    I am using the load balancing server with master/game and my hunch is the host.TryRegisterType adds it in a different scope than the Protocol.RegisterType so when the server is sending events, it's not available.
  • chvetsov
    Options
    hi, @tudda

    if you are going to host photon yourself then yes, you may register your type directly in protocol. if you will use our cloud, than your trick will NOT work. you should register you custom type during plugin setup or OnCreateCall, then we will do the rest

    if it does not work for you in some cases please describe what do you do, how do you send, what data were not received and so one

    best,
    ilya