Hacker News new | ask | show | jobs
by 1_player 1877 days ago
> What if Player 2 made a conflicting play and their change was accepted by the GameServer before Player 1's change reached the GameServer?

Not sure I understand the question but, I don't see how this would happen.

On the BEAM it's processes all the way down. There's a process for that instance of the game, which is basically a big state machine, and 2 processes representing the client state, one for each player.

When the game (process) starts, it expects a message from player 1 (process), then one from player 2, and so on.

If there's a client timeout or network disconnection, the player process affected crashes, and if the app has been architected well, the other player process and game process are in a supervision tree, so they crash as well, perhaps notifying the other player that the game has ended because of a disconnection from the other peer.

But none of this will accept a move from the player 2 when it's player 1's turn.

1 comments

Thanks for your reply!

This is very interesting - I'm pretty unfamiliar with BEAM. Does this "processes all the way down" span across machines/VMs?

From the article, it seemed like there could be two players, each connecting to different LiveServer instances (on different VMs/hardware in different geographic regions) which in turn communicate async via one central GameServer.

In the article, it seems like a message from Player 1 to LiveServer 1 doesn't need to wait for the message to also reach the central GameServer and be acknowledged before LiveServer 1 acks the change back to Player 1. This seems to allow races, since the central GameServer is the source of truth but the Player1/LiveServer1 communication can complete a message/ack round-trip without waiting for acknowledgement from the GameServer.

I guess an alternative would be for the system to require a message from Player 1 to be passed to LiveServer 1, then passed on to the central Game Server which acks back to LiveServer 1, which finally can ack back to Player 1 -- this means that Player 1 would still need to pay full round-trip latency to LS1 and then to the GameServer for any action.

Thanks for any light you can shed on this!

Here's the relevant part from the article:

> The browser click triggers an event in the player's LiveView. There is a bi-directional websocket connection from the browser to LiveView.

> The LiveView process sends a message to the game server for the player's move.

> The GameServer uses Phoenix.PubSub to publish the updated state of game ABCD.

> The player's LiveView is subscribed to notifications for any updates to game ABCD. The LiveView receives the new game state. This automatically triggers LiveView to re-render the game immediately pushing the UI changes out to the player's browser.

So you can see that when Player 1 does an action, the action is sent to the GameServer. Player 1's UI is only updated when the GameServer has published the new game state via PubSub back to Player 1's LiveView process, that pushes it onto the client. So there is the latency of going from client to LV to GameServer and back again, but there is no race possibility.

> I'm pretty unfamiliar with BEAM. Does this "processes all the way down" span across machines/VMs?

yes for example if u had a process named on a different Machine(Node called in Erlang) called "Alice", u could from a different Node send it a message using the Node Identifier as additional parameter example:

[coolest_node | _rest_of_nodes] = Node.list()

Process.send({Alice, coolest_node }, :hi)

Ah, that's indeed a good question, though those are implementation details of Fly.io I'm unaware of.