Your question is quite broad. You're eventually looking for a feasible architecture for a turn based server-client type of multiplayer game. A complete answer would require drawing a few diagrams and writing some skeletal code. I'm sure there are some detailed enough examples around somewhere.
However, there's one particular aspect about the design you should consider whether your application is going to be a local or a distributed multiplayer game or whatever.
It seems like you're about to mix the model and the control. I strongly recommend separating them. The module that holds the model for the game should not have any knowledge about servers or clients and such. The rules of a card game do not depend on whether the architecture of your game application is distributed or not. Actually, they don't even depend on whether you write an application for playing the game on computer or for simulating the game or for other purposes like tracking and keeping score of a live game.
So, I'd begin with first writing a working model for the card game itself. As long as you keep the model separated, you don't need to think about how you're going to implement some fancy distributed multiplayer game later on.
The model of a card game consists of few elements: a model for the cards and possible game tokens, a model for the game state and a model for the rules. Since we're talking about a turn based game, the rules of the game are mainly about defining legal actions and the effects actions have on the game state. I would expect the model to contain classes like
Card
(for cards)
Deck
(for a deck of cards)
GameState
(aggregate for the game state; preferably serializable)
PlayerState
(for holding the state of a player; child of GameState)
TableState
(for holding the state of the game table if there's any; child of GameState)
Action
(for actions a player could take)
The rule model should consist of methods and classes that control what kind of Action
s are available at any state of a game and how a chosen Action
would change the state of the game. The rule model should offer methods like:
List<Action> DetermineAvailableActions(GameState gameState)
GameState ResolveGameStateAfterAction(GameState initialGameState, Action action)
A simple rule model could consist of one class (GameRules
) implementing above methods.
Should you choose to write a mutable GameState (which I wouldn't recommend) the latter method signature would be like
void ApplyAction(GameState gameState, Action action)
The model for the game should be absolutely agnostic about the rest of the application, the environment and particularly the user interface. The same model should work for a local single player game application as well as for a multiplayer server-client game application. You should be able to run simulations of whole games from the beginning to the end just using the model and a simple game loop, some logging and a minimally simple implementation of an AI or rather an artificial idiot just picking random actions.
After writing and testing the game model itself, you could continue with writing a game controller. The controller should contain the actual main loop of the game. It should be able to
- take initial game settings (number of players, player types etc)
- initialize the game state as per given settings
- control the flow of the game and the communication between modules
- send messages to players by invoking methods of some
PlayerController
objects
- listen to messages sent by players
Details of the controller depend on the architecture of the game application. The controller should know about all modules involved. However, if you play your cards right, you could use the same controller for controlling a simple local game or simulation as well as a distributed multiplayer game.
A simplified game loop could look like this:
while (!currentGameState.IsFinished()) {
activePlayerController = playerControllers.Get(currentGameState.GetActivePlayerId());
List<Action> availableActions = rules.DetermineAvailableActions(currentGameState);
activePlayerController.SendPleaseChooseActionMessage(currentGameState, actions);
Action chosenAction = ReceiveChosenAction();
currentGameState = rules.ResolveGameStateAfterAction(currentGameState, chosenAction);
gameStateHistory.Push(currentGameState);
for (PlayerController c: playerControllers) {
c.SendGameStateUpdatedMessage(currentGameState, chosenAction);
}
}
If you're not an experienced programmer, I'd suggest trying to write a simple local game first. Just keep the model, the controller and the user interface separated.