Authoritative Server, Card Game, How to store game state/objects and sync individual actions
Options
tudda
✭
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.
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.
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 Card6What 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.
0
Comments
-
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,
ilya0 -
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?0 -
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?
0 -
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
0 -
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?0 -
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.0 -
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,
ilya0