Skip to content
dripton edited this page Aug 17, 2010 · 1 revision

Goals:

  • Minimize coupling
  • Keep the code simple
  • Avoid duplicate code
  • Prevent cheating
  • Support convenient unit testing
  • Allow standalone battles
  • Allow replaying or stepping through savegames, and use the same mechanism to support undo / redo / stepping through the current game
  • Minimize network traffic
  • Maximize performance, especially of the AI
  • Spread the CPU and memory load among server and clients

(Obviously there are tradeoffs.)

Security

1. Preventing illegal actions: Simple object models that let the client directly manipulate the state of the server will result in cheating. There needs to be a layer of permission checking. PB’s perspectives keep users from spoofing one another. Each action must be manually checked to see if that user is currently allowed to perform that action. We need a common idiom for this to make these checks hard to forget and easy to code.

2. Avoiding information leakage:

  • The server must avoid sharing hidden information (other players’ legion contents, the state of the random number generator) with clients.
  • Revealing the contents of a legion is a separate action, with a restricted list of observers. Chatting is similar, but not really part of the game protocol. Other game actions are all public.

We should avoid leading clients around by the nose with prompting actions like “Flee?” They should be smart enough (using the shared Game logic) to see that there’s an engagement and their legion is the target so they need to send a Flee / Concede / Fight / Wait.

Client-to-server calls go to Server (for the few whole-server actions like logging in) or to User (for the many per-user actions). From there they should be routed to Game.

Server-to-client calls will go to (one or more instances of) Client. We do not allow direct client-to-client calls. This means that the client will not need to permission-check external calls. It also means that Clients do not need holes in their inbound firewalls, just the ability to connect to the Server. From Client they should be routed to Game.

We reuse the same basic logic classes (Game, Player, Legion, Creature, Movement, etc.) on the client and server sides. This means extracting the ability to directly modify real game state from them. It also means the Legion class must be usable without certain knowledge of its contents.

We use the standard Observer pattern. We use add_observer / remove_observer / notify. If the list of observers is empty, then no actions are broadcast from this instance.

Actions should cascade back up the observation hierarchy (e.g. Creature to Legion to Game to Client to User) in a way that makes it easy to remove observers for hypothetical situations (AI, stepping through history).

The related jobs of creating savegames and logging game actions are handled by History, which observes Game.

Every operation that can affect real game state is an action. Savegames are just action logs. If we load a savegame, or a new spectator joins in mid-game, sync game state by replaying the action stream. Need a “new game” action to signal a client that may have already been observing to clear its state and start receiving the action stream from the beginning. (We might optimize this later to take advantage of a spectator’s existing knowledge.)

When a player attempts to change game state from the GUI (example: moving a legion to a new hex), the action trickles up to the client’s Game (where it will be error-checked and an exception raised if illegal), then from Client up to User on the server side.

Safe GUI actions that do not attempt to change game state (e.g. viewing a legion’s contents) should require no communication with the server. This should extend to more complex actions like rewinding through game history.

Should we extend this to letting a player make multiple “unsafe” actions locally then attempt to commit the action stream to the server? (Analagous to line-buffering in a chat client — other users don’t see how slowly the user types or how often he backspaces, just the line.) No — it’s good to show the action stream as close to realtime as possible, to keep other users interested and keep everyone playing fast, even though this exposes a bunch of undo actions. AIs may be an exception since watching an AI constantly undo moves would be annoying. (Except perhaps in a debug mode?)

Undone actions should be trimmed from savegames for neatness. But we can’t prune undone actions from the client’s action record too early, or we break the ability to redo them. Just keep a separate local redo stack on the client only.

When a player tries to make an illegal move, the exception should only be shown to his client. So attempts to make illegal moves should not reset inactivity timeouts.

We should be able to handle standalone battles by creating mock versions of non-battle classes.

We need a non-GUI console client, for debugging and automated testing and AI use. This implies the ability to stringify and unstringify actions.

Clone this wiki locally