I'm developing a small multiplayer game. It'll be served by one websockets server and consumed by multiple consumers. As such I need to be cautious about concurrency errors.
The general software architecture I've come up with is: Encapsulate all game state into the highest-level
Game class and don't allow other threads to access gamestate objects. Instead, clients (websocket threads on the server) send commands to the
Game class where they're added to a threadsafe list and later processed by the
Game.Tick method which is called in a separate thread. For example:
Game.AddCommand assigns a command to the list of incoming commands:
public void AddCommand(Guid id, ICommand command)
_commands[id] = command;
Game.Tick processes each command and clears the list:
public void Tick()
commands = _commands.Values.ToList();
_commands = new Dictionary<Guid, ICommand>();
foreach (var command in commands)
The issue is this: To resolve themselves each
Command needs access to the internals of
Game. I intend to use a number of services such that a
Resolve method can be very few or even a single line, such as
movementService.Move(entity, target). If I passed these services into the
Resolve method, each
Command would become bloated with things that it doesn't need.
Instead, I'd like to use DI. But since each
Command is created by a client, they can't access the services that belong to
Game. These services need to be exposed, and if the client does something wrong (ie call
Resolve themselves) they could change gamestate and cause concurrency errors.
I tried to resolve this by creating the notion of a
CommandResolver, in which case a
Command would be a simple model with some parameters and nothing more, resolved by an internal
CommandResolver which has access to gamestate. But this required elaborate mapping (eg map
MoveCommandResolver- each command is a different type with different dependencies) which defies the Open/Closed principle- you have to modify the mapping, likely a long awkward switch statement, rather than simply extending the functionality with a new
Now I'm scrapping the resolver and considering one of two possibilities: a) Expose from
CommandFactory that can be used to generate commands, prehaps with a syntax like
game.GetCommandFor(entity).Move(target). The commands then have access to gamestate and resolve themselves. b) Commands are constructed by clients, but before being resolved an
Initialise(IUnityContainer) method is called that gives the
Command access to gamestate. This way it only gains access to gamestate once the command is ready to be issued. Of course, the client could still technically hold onto the command and call it at a later date.
Any thoughts on what the best solution is? My primary concerns are easy extensibility while ensuring the integrity of
Game's internal state.