Background: I help develop a multiplayer game, written mostly in C++, that uses a standard client-server architecture. The server can be compiled by itself, and the client is compiled with the server so you can host games.
Problem
The game combines both client and server code into the same classes, and this is starting to be very cumbersome.
For example, the following is a small sample of something you may see in a common class:
// Server + client
Point Ship::calcPosition()
{
// Do position calculations; actual (server) and predictive (client)
}
// Server only
void Ship::explode()
{
// Communicate to the client that this ship has died
}
// Client only
#ifndef SERVER_ONLY
void Ship::renderExplosion()
{
// Renders explosion graphics and sound effects
}
#endif
And the header:
class Ship
{
// Server + client
Point calcPosition();
// Server only
void explode();
// Client only
#ifndef SERVER_ONLY
void renderExplosion();
#endif
}
As you can see, when compiling the server only, preprocessor definitions are used to exclude the graphics and sound code (which seems ugly).
Question:
What are some of the best practices for keeping code in a client-server architecture organized and clean?
Thanks!
Edit: Examples of open source projects that use good organization are also welcome :)
I would consider using a Strategy design pattern whereby you would have a Ship class with functionality common to both client and server, then create another class hierarchy called something like ShipSpecifics that would be an attribute of Ship. The ShipSpecifics would be created with either a server or client concrete derived class and injected into Ship.
It could look something like this:
The classes Ship and ShipSpecifics would be in the code base common to both client and server, and the ShipSpecificsServer and ShipSpecificsClient classes would obviously be in the server and client code bases respectively.
The usage could be something like the following: