Hacker News new | ask | show | jobs
by nicolodavis 2184 days ago
There is value in making the game progress client-side without waiting for a response from the server. Having the game logic run on both sides gives you the best of both worlds: 1. One authoritative version of the game state on the server. 2. Lag-free experience on the client.
1 comments

You can make the game progress without running validations or duplicating game logic on the client.

For example, in the online multiplayer version of Ludo I'm currently building, I trigger the die roll animation on the client but the actual dice are rolled on the server, which then calculates what moves are possible based on the game state and sends that + the value of the roll to the client. The client doesn't know anything about the rules of Ludo, it simply knows how to parse and render the move format as well as send the ID of the picked move to its source of truth (in this case, the server).

https://en.wikipedia.org/wiki/Ludo_(board_game)

I spent the last two years coding a reimplementation of A Game of Thrones: The Board Game[1], and I would use this paradigm if I had to recode it. Sharing the code between the client and the server brings a lot to the table.

Having to wait for the server for the set of legal moves feels like a hack. It's way simpler to have the client have its own copy of the game state, and allow "query" methods (methods that fetches data about the game state but don't modify it). This allows you to make _powerful_ UI, which can show any kind of information the user needs about the game like predictions about what would happen if a move is done, computing things so the user doesn't need to.

If I had to redo it, I would use a paradigm similar to how boardgame.io[2] works, which is similar to how fixed lockstep works. The server and the clients all keep a full state of the game and when a player makes an action, it is transmitted to all the actors, who apply this action to their own state of the game. Since the game rules are deterministic, the final state of the game will be the same for each actor. The only 2 complex to handle are randomness and secrets (the former can actually be solved by the later), but overally, the complexity is managed elegantly and you can separate networking code from gameplay code easily.

Actually, one of my future possible project would be to make a library similar to boardgame.io, but less opiniated, and in the future, offer a platform to easily host and launch a game coded with it.

[1] https://boardgame.io/

[2] https://swordsandravens.net/

> Having to wait for the server for the set of legal moves feels like a hack

But you are waiting for the server anyway - that's the "turn" in "turn-based". The way I build, the notification about your turn simply comes with the moves you can make as well - and the moves you can make don't change, because _it's your turn_, so there's no limit to how powerful a UI you can make with it.

Take chess for example (it was while implementing chess that I worked out this paradigm, actually) - a move from the server (sent in a packed binary format) was a mapping of start and end positions and a metadata byte that linked it to a kill, a promotion, or castling. and it was rare indeed that a complete payload (with a full set of currently legal moves + effects) would be up to two kilobytes. The client would deserialise that and of course could show you possible moves when you selected a piece and so on.

> The server and the clients all keep a full state of the game and when a player makes an action, it is transmitted to all the actors, who apply this action to their own state of the game.

This is actually what my current approach was born out of! But then I wanted to build a second client app, and I felt it was a pain to rewrite the game logic in another language - one solution to that is to have an isomorphic application, but in my case I decided to try out moving as much as possible to the server and just focusing on data parsing and rendering on the client and it worked out surprisingly well. It also let me do things like implement custom rulesets with much greater ease, because the rule logic exists solely on the server and you can deploy/modify that without requiring the clients to update.

Fair enough!

I can understand the reasoning if you need to code a client in a different language, then you would effectively need to code the whole game once again. I feel like it would quickly bore me to code the rendering of each set of moves for each possible action. I feel like it's easier to have access to the complete state of the game client-side when you need to code a UI. There might be a solution if the server broadcasts the diff of the game state to the clients after each action has been performed (instead of letting the clients re-apply the action on their game state), but I'm not sure it would be nough.

How do you handle state synchronization, though? When a player makes an action, how do you propagate the changes to the game state?

Ditto; the client side code needs no game logic, only aiding UX; dont let the user click outside a minesweeper board, but if it does, the server still validates.

I made a System inspired by boardgame.io (but it was too integrated with react for me), which means i can write the game on the server without writing any client code at all. the client gets a list of valid moves, and my debug view gets error messages and a bunch of buttons & drop downs for parameters.

https://github.com/NewChromantics/PopNotPoker/blob/master/Se... here's multiplayer minesweeper:) Http://not.poker

All the real work is in the lobby code anyway.

@soylentgraham: You should check out the recent releases of https://boardgame.io/. The tutorials are now rewritten with a focus on the plain JS client (no React).
oh cool, I gave up with boardgame.io in the end (I liked the API design though) because I spent 99% of my time trying to rip it out of react (both server and client) so I could use it in a non-web situation. (I have a game engine with high-level JS, and the game server/client is only one small part of it)

Maybe I can go back, although I'm writing game logic much faster now I'm in NIH territory, it would be nice to contribute to someone else's project

Damn, I missed this. I hated having to install create-react-app (with 100s of MB of dependencies)
If I understand your architecture correctly, your client has to wait for the network roundtrip before being able to see the result of the die roll, correct?
Yes. This is over websockets though, and in all of my testing/profiling (pretty shitty ISP in West Africa, game server is currently hosted in the US) I haven't had an exchange take longer than one second. Also the mandatory wait is just for the die - for picking a move, for instance, the client can go straight into rendering the selected move while sending it to the server.