🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Turn based Multiplayer: Receiving Events from Server

Started by
5 comments, last by hplus0603 5 years, 9 months ago

Hello!

My plan is a game where multiple players play turn based, often with a hours in between turns and sometimes seconds.

My idea was an RPC/REST-server, where users request data and send data.

One question arises though, what if a player disconnects? How do I inform the player? I fear I will need a TCP-connection? Or should clients regularly poll for updates via the HTTP-API? I feel like having an event-log table in my database might be a solid option and the player then requests new entries after entry number X?

Thanks for your time : )

Advertisement

This problem has a number of different solutions, depending on specifics.

Polling every 20 seconds when the player has the game open is okay for many turn-based games.

If you want to improve responsiveness, you can also create a websockets connection, and send an update to the player when something happens in the game. You typically end up writing this as some kind of message broadcast tree, where you'll broadcast all changes but players will only hear about them if the websockets channel is actually open.

Another option, if the game is mobile, is to use push notifications.

Polling once a second gets old quick, though -- that'll use a lot of resources! (polling every 20 seconds is still kind-of wasteful, but a better way to get started, if you don't have, say, interactive chat or similar real-time needs.)

 

enum Bool { True, False, FileNotFound };

I did not even think about the chat, yes, there is a chat.

Also it works via cross-play, so both mobile and desktop-players.

Websocket seems a bit risky if the receiver is on mobile. E.g. Android could kill the application or internet cut off. So some resync-strategy would be required.

I really liked my HTTP-simplicity but that seems unfit then I guess.

Polling every x seconds is too slow, some sessions are as fast as players react and not in a slow-mode tolerant way where players can react within an hour.

Is there a protocol suited here? Maybe something like WebSocket? How is syncing done?

Those requirements fight against each other! What do you do if an Android player goes into a tunnel and loses web connection for 15 minutes? What should the game do? What happens to chat? What if the user closes the game for the night and expects to continue next day?

In general, yes, you need some kind of state recovery, or at least state roll-forward. You can have a sequence number for each state/event update and message, and the client caches its state and what the latest received message is. Whenever it connects to the server, it asks for more messages.

I think websockets is fine for this. As long as the client is running and foreground it's unlikely to be cut off, and it can have some timer so that, if it hasn't heard for 20 seconds from the server, it stops and restarts the websocket connection. Or, if someone is behind some proxy where websockets don't work, you can also have a fall-back to HTTP polling.

The question here is whether the game session itself is really "turn based and asynchronous," or whether the session is synchronous, but the game mechanics are turn based. For example, Quake is synchronous and real time; Hearthstone is synchronous and turn based, and Neptune's Pride is asynchronous and turn based. If your game is like Hearthstone, then you probably don't actually want to think like a turn-based game; you need a synchronous game communication protocol, and the fact that the game rules happen to be arranged in turns doesn't really matter.

For synchronous game protocols, there are several ready-made solutions, all the way from Enet, through RakNet, through all of the built-in or add-on networking features in engines like Unity or Unreal.

enum Bool { True, False, FileNotFound };
21 minutes ago, hplus0603 said:

Those requirements fight against each other! What do you do if an Android player goes into a tunnel and loses web connection for 15 minutes? What should the game do? What happens to chat? What if the user closes the game for the night and expects to continue next day?

That depends on the lobby-setting. One can set a maximum amount per turn, e.g. 30 minutes. Closing the game is fine, one can always reconnect to a lobby.

 

23 minutes ago, hplus0603 said:

In general, yes, you need some kind of state recovery, or at least state roll-forward. You can have a sequence number for each state/event update and message, and the client caches its state and what the latest received message is. Whenever it connects to the server, it asks for more messages. 

I thought about serialising the game on the database. Games save their own state on disk, if someone wipes all data, then one would need to request the game from the database.

 

28 minutes ago, hplus0603 said:

The question here is whether the game session itself is really "turn based and asynchronous," or whether the session is synchronous, but the game mechanics are turn based. For example, Quake is synchronous and real time; Hearthstone is synchronous and turn based, and Neptune's Pride is asynchronous and turn based. If your game is like Hearthstone, then you probably don't actually want to think like a turn-based game; you need a synchronous game communication protocol, and the fact that the game rules happen to be arranged in turns doesn't really matter.

Well, my game is somewhat like Hearthstone. Players can look at their "items" and plan ahead, and use the chat. At some point the server notifies them that their turn is due and the timer ticks.

30 minutes ago, hplus0603 said:

For synchronous game protocols, there are several ready-made solutions, all the way from Enet, through RakNet, through all of the built-in or add-on networking features in engines like Unity or Unreal.

I looked at WebSocket and I think they suit my needs quite fine. I will have to see on how authentication works with them and how to wisely differentiate channels from another, but maybe authentication and token acquiring should be a different WebSocket-server and maybe even HTTP then lobby-managing?

When I think about acquiring things like daily quests, that's easily done via HTTP. While lobby-creation seems to be rather done via WebSocket.

Is it common to simply set up multiple servers as multiple executables? Eg. login/token-acquiring-server via HTTP, quest-acquiring via HTTP, lobby/game-session-server via WebSocket, ...?

You can do authentication and lobby over HTTP/S/2; you only need the web socket for the low latency push bits. (But when you have it, you can of course do more with it.)

Once authenticated, issue some kind of token that the websocket connection can use to authorize itself on connection setup, similar to how you'd use a session identifier cookie in HTTP.

There are three kinds of ways of setting up services:

1) Each "endpoint" or "service" lives in its own process. Use cheap ways of spawning lots of processes -- kubernetes, nomad, swarm, ... Use some kind of smart HTTP router on the way in to send the right requests to the right processes. (NGINX with path and host matching rules, for example.) Most solutions like these call themselves "microservices." This makes it very easy to develop things in parallel across large teams, but instead has a higher operational cost, as well as the communication cost to make sure that all the different services stay to the same data structures, rules, and protocols.

2) There's a binary/program, that can "do anything." You might spin up multiple copies of it (if it's stateless or uses shared memory state or sharding,) but it's fundamentally "one thing." This is usually known as "monolith architecture," and is super easy to get started with, and to test in a developer sandbox. When your system gets really big, it starts causing development/deployment friction on large teams. (Wordpress and other PHP sites fall into this category, even though they may have different entry point scripts for different pages/services.)

3) There's a binary/program per major functional component. Maybe sign-in and lobby is one, game serving is one, and microtransaction store is one. Each component may be built using a different technology stack, or not, but the routing is typically set up entirely by host -- lobby.game.com, store.game.com, play.game.com etc. This is somewhat of a middle ground between microservices, and monoliths. It lets you choose "the right tool for the job" for specialized systems, but it still has some cost and overhead in that your system is diverse.

Separate from those three ways of slicing things, there's the question of horizontal scalability. I e, if you want to be able to "start more processes of the same kind" to serve more load, then whatever service/es is run by those processes, need to be somehow okay with load balancing, where different players end up using different instances of the same service code. Typically, for web/database systems, this is not so bad, as the state lives in the database, but for persistent game processes, you end up doing "sharding," where players can only directly interact with other players on the same physical server. If you have a system that can do 10,000 players per server, and want 100,000 people all in the same city market square in your world, all being able to talk to each other and interact with each other, you can't just spawn 10 servers that deal with 10,000 players each, because there's a 9/10 chance that the player will want to interact with some player that's on another one of those servers, and all servers end up needing to know about all 100,000 players, which ends up not saving any capacity. (There are in turn various ways of trying to solve this, which have significant gameplay and technology trade-offs. My recommendation is not going there unless you are 100% sure that you absolutely need to!)

 

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement