Help With Server Design

Hi. So I basically made a turn-based strategy game and a server to go along with it. The problem is the server is a mess and I can't manage it anymore. The game itself is text based where 2 players go against each other by picking a 'class' and using specific abilities.

For my new server I plan to use a thread for every game. This lets me be able to have each game separate from each other and makes it so I can pause each game thread when I'm getting input from every player (seeing it's turn-based). If I went down this way, should I still get all player input in the main server thread and then pass the info to each corresponding game/thread or should I get input from the game itself? I know a thread for every game is pretty overkill in my situation performance wise, but i feel it'd be good because being able to pause a game when both players are inputting would be good.

Also, for knowing what menu each client is on I have an enum 'state' for each client. So for example, if the client is on the state 'MainMenu', and they click 1 (which is the button to find a game), the server would get that input and do whatever it needs to do. is this the best way to get input in a client/server relationship?

Thanks.
Last edited on
One thread per game should lead to a cleaner design if the separate instances don't need to share any data or communicate in any way. I'm not sure what you mean by "pausing" the game, given that it's turn-based.

Also, for knowing what menu each client is on I have an enum 'state' for each client. So for example, if the client is on the state 'MainMenu', and they click 1 (which is the button to find a game), the server would get that input and do whatever it needs to do. is this the best way to get input in a client/server relationship?
Why is this even in the server? The server should only maintain world state, implement game rules, and relay information between the clients. A menu is something local to the client, is it not? It'd be like writing an e-mail by sending each individual keystroke to an Internet server and getting back a bitmap of the letter than needs to appear on the screen.
Because if the client wants to join a game it has to tell the server. Before the client is in game the server needs to know what the client is requesting (joining a new game, requesting leaderboards, etc). Is having a state of each client not recommended for that? Or would it be better to have a custom network struct to send with an enum defining what the client is requesting?

Also, should each game thread deal with checking if there's incoming input? Or should a main server get input and relay it to each thread?
Last edited on
Because if the client wants to join a game it has to tell the server. Before the client is in game the server needs to know what the client is requesting (joining a new game, requesting leaderboards, etc). Is having a state of each client not recommended for that?
It's not that it's not recommended, you're just complicating the design for no good reason. The protocol could look like this:
<User requests leaderboard>
C: get leaderboard
S: Alice 4000
S: Bob 3000
S: Carol 1000
S: Dennis 500
<User is presented with leaderboard. User requests a list of available games>
C: get games
S: game foo 4
S: game bar 7
S: game baz 3
<User is presented with the list. User navigates the list and selects game bar>
C: join bar

A simple and narrow protocol leads to a clean and decoupled design.

Also, should each game thread deal with checking if there's incoming input? Or should a main server get input and relay it to each thread?
Each thread should ideally perform its own I/O without involving other threads. The more threads have to communicate, the more complex and buggy the code becomes.
"<User requests leaderboard>"

This is the part I'm confused at. I understand the rest. How does the server know you're requesting to see the leaderboard if the server doesn't know what menu your client is at or if you're not sending an enum (like TYPE_REQUEST_LEADERBOARD) with the packet??

Last edited on
A server's only job is to accept and fulfill requests. To fulfill the request "get leaderboard", the server doesn't need any information from the client. The specifics of how the user requests to see the leaderboard, how the menus are laid out, or even whether a user exists at all, are of no use to the server for the purposes of fulfilling the request. Only the client needs to know how to interact with the user.

if you're not sending an enum (like TYPE_REQUEST_LEADERBOARD) with the packet??
My example uses a string-based protocol simply to make it easier to read. There's any number of ways to implement a request protocol. Sending a request code with attached parameters is an option.
So should my code something like this?

1
2
3
4
5
6
7
8
struct InputHeader
{
enum TYPE
{
TYPE_LEADERBOARD,
TYPE_JOINGAMEQUEUE
};
};


Then I could send that to the server and in return the server would do whatever it needs to do.

1
2
3
4
5
6
7
void getMessage(InputHeader& _header)
{
if(_header == InputHeader::TYPE_LEADERBOARD)
     send(leaderboardInfo);
//etc
}


?
Last edited on
Sure. You might want to take a look at Google's protobuf. It's specifically designed to implement efficient interprocess communication protocols.
Topic archived. No new replies allowed.