Opinions regarding efficient match loop logic

Hello all,

I’m looking for an opinion/input regarding best practices on this kind of implementation to make it a bit more efficient in terms of network data exchanged in an authoritative match.

Let’s imagine a card game (Unity) with 7 different drop zones. Users can move cards between these zones to make their “move”. The order of cards in each zone matters. So if you have a zone with 10 cards, you take out the card at index 5 and move it to the top of another zone.

In the zone you took the card out of, the index for all the cards “to the right” shifts up by 1. In the zone you dropped the card, all the existing cards shift down by 1.

To make it even more difficult, there can be empty slots in zones between cards.

What would be an efficient way to sync inventory items during a match and save their indexes? So far I have:

Method 1:
Hold the data regarding the current zone and index on the CARD object. But this would mean sending data for all the cards which have changed their index following a card move. These could be dozens.

Method 2:
Hold the CARD list inside each ZONE object and manipulate the cards within. This has another issue because you can’t account for empty slots. And you still have to send the other cards which had their index changed.

Method 3:
Just send the card movement and its new index, validate in the match loop, and send success message. And let the clients handle the ordering based on this move. This one makes the most sense, but it also allows for desynchronizations between client and server if messages are lost or received in the wrong order.

I guess this could be a case of premature optimization or my lack of understanding of how big is too big for a server response. Syncing all cards came up to about a 10kb JSON response. For a tick rate of 3-5, is this too much?

Any idea and better methods are appreciated and I thank you in advance for your time. Just to mention, I’m not looking for code here, just design ideas.

Kind regards

Hi @Antviss,

It’s difficult to answer your question without a little more context. The answer really depends on several factors such as how often players are moving cards between zones, whether all players can always see all cards and they therefore need to remain perfectly sync at all times etc.

I would lean towards Method 3 that you have highlighted. Whereby you send each player’s move to the server, where it is then validated and broadcast to each connected client where they can update their local view of the game.

You mention this allows for desynchronisation if messages are received in the wrong order, and while this is true, it is a common problem when dealing with networked games and one that can be remedied by having the server send regular game state updates to players.

I’m curious though, you mentioned syncing all cards takes up 10kb of JSON. Can you explain or show what exactly this consists of? 10kb seems like an awful lot of data for sending just the position state of 7 * 10 cards.

HI @tom . That is a very thorough response and it is very much appreciated! I think I will end up going for method 3.

For more context, the other players can see all but 2 of the zones at all times. Which means that player movements within all the other “public” zones need to be broadcast to all other users. Movements between zones happen during a player’s turn and can be 1-2 moves max.

Regarding the 10kb issue, that’s the approximate size of the entire game state. You are correct that it wouldn’t be near as much to send just the positions of the cards. But this would mean iterating over all the cards and building a separate object to send out which contains the card ids and their positions. And the unpacking the object client-side and iterating over all the cards in the local state and updating their positions. Is this good to do during the loop or would this be considered an “intensive” operation?

I think it all boils down to me being worried that the actions I write in the match loop are too heavy. So a case of over-optimizing something which might not need to. And it probably stems from my lack of understanding of the match loop. Worried that since the loop runs 5 times every second, there won’t be enough time for certain operations to complete. Which isn’t the end of the world if they happen on subsequent ticks (being a semi-turn-based game). But it would be an issue if they are stopped/dropped if they don’t complete.

Hi @Antviss, thanks for the clarification and context.

Given what you’ve said, I would definitely go for Method 3. Since it’s a turn based game you could also likely afford to have each client verify it has received the previous player’s move before the server then allows that client to make its next move; whether this is necessary or useful depends entirely on your game and how the turns work.

I would suggest looking into maintaining a separate “game state” object that contains just enough information that a client would need to replicate the accurate state of the game with if needed and send this every so often so clients can verify they haven’t gone adrift.

Whether it’s efficient or not to do the unpacking/packing each loop as you mention is not really something I can advise on since I don’t know the complexity of your state model, but should you choose go down this path I would advise testing the performance of it first before going down the over-optimisation route.

Hope this helps.