stats or profit give away result of game

Think of a coinflip game, whose logic looks as follows:

• Server sends websocket message (RESULT_MESSAGE) to frontend with result of a game (won/lost)
• Server sends websocket message to frontend with updated aggregated stats (STATS_MESSAGE) (think all-time profit after the new coin flip, or updated all-time stats)
• Frontend reacts to RESULT_MESSAGE by playing an animation (coin flipping). It also updates the all-time stats and profit in response to STATS_MESSAGE

The problem with this logic is, responding to STATS_MESSAGE (updating profit and stats) gives away the result of the coin flip before the animation finishes.

Solutions I cannot use:

• Merging RESULT_MESSAGE and STATS_MESSAGE together, and only updating the stats/profit after animation finishes. Because those 2 messages are produced and sent by different services.
• Linking a particular RESULT_MESSAGE to a STATS_MESSAGE and only reacting to a STATS_MESSAGE when its corresponding RESULT_MESSAGE has been processed and its animation completed. Because in reality I have several kinds of STATS_MESSAGES (trophies updated, stats updated, profit/balance updated, etc), and it's just not practical for the frontend to hold onto all those messages until their respective RESULT_MESSAGE is processed

Solutions I've tried successfully before, but I'm trying to avoid:

• Having the backend send STATS_MESSAGE with a delay, to make sure it doesn't reach the frontend before the coinflip animation has finished. This sort of encodes frontend behavior (animation) on the backend.

All ideas and suggestions appreciated. Thanks a lot!

EDIT: I made the coin-flipping example for simplicity, but it's not precisely con-flipping. It's a multiplayer game where dozens of games/animations per user per seconds are going on. So queuing a bunch of stuff on the frontend might be less than ideal (I'm still looking into performant versions of this solution though).

Pulling the STATS data via an API call when the animation finishes (instead of using websockets data) is another alternative that I've considered, but for the same reason described above it might not be feasible (dozens of API calls per user per second). Plus there's a chance that an API call is triggered by game-1 finishing, but while game-2 animation is still going on, giving away the result of game-2

• "only updating the stats/profit after animation finishes" - why can't you use this approach? It's probably roughly what I would do. – Dukeling Jun 24 at 16:57
• Thanks Dukeling, I've updated my question with reasons why it's not straightforward to do what you suggest – Luis Alvarez Jun 24 at 17:18
• I still do not understand why the frontend can't hold the messages. Can there be multiple games in progress for a single client? Perhaps the client could query the stats after the animation has finished - but which ones? And you need to make sure the stats have been updated by now. – Aleš Koblížek Jun 24 at 19:36
• Great point Aleš Koblížek, I made the coin-flipping example for simplicity, but it's not precisely that. Dozens of concurrent games per second, per user can be going on at the same time, so holding everything in memory is not feasible. I'll update the question – Luis Alvarez Jun 24 at 19:44
• I assume the games can also end in any order, not necessarily the one in which they started. The animations are also concurrent. The stats are some sort of numeric data so the size of an update is as expensive as complete state? (I am thinking about using something similar to git branching.) – Aleš Koblížek Jun 24 at 20:43

1 Answer

You are actually dealing with two gamestates models here: The actual model representing the current state of the game (where the player has already won/lost) and the virtual gamestate model representing what the player should be aware of (where the coin is still spinning). If you are aware of this and separate these two game-states on the server side, you can solve the problem as follows:

1. Player clicks on "spin the coin"
2. Server receives the coin spinning message and updates the actual game state
3. Server starts a timer of x seconds
4. The timer finishes and triggers an event to update the virtual game state
5. The change to the virtual game state triggers a message to the player, informing them that the coin stopped spinning, the result of the spin and their new game state.

I know you said you want to avoid to "encode frontend behavior (animation) on the backend". But if that frontend behavior actually gives the player a gameplay disadvantage (having delayed access to information which is already known to the server) and you want the game to be cheat-proof (you wrote it's a multiplayer game, so I assume that's a priority), then you can't trust the frontend to implement that disadvantage. You must make sure that the client only receives the information it is supposed to tell the player. That means you have to implement some of the presentation logic server-sided.