Skip to content
Minimalistic game server in elixir
Branch: master
Clone or download
pb82 Merge pull request #6 from lidashuang/master
fix ModuleStore.get/1 is undefined
Latest commit 096628b Sep 25, 2017
Type Name Latest commit message Commit time
Failed to load latest commit information.
config tests pass Jan 3, 2016
lib fix ModuleStore.get/1 is undefined Sep 11, 2017
test tests pass Jan 3, 2016
.gitignore Socket code revised, using erlang packet length option Dec 15, 2015
.travis.yml Update .travis.yml Aug 8, 2017 Update Sep 10, 2017
mix.exs fix warnings Aug 8, 2017

Servus Build Status

A simple, modular backend for multiplayer games. Simple, because it just handles the (socket) communication between a number of players and provides hooks for the game logic. Modular, because game logic is written as a state machine and loaded into the server at runtime. The server can host multiple game logic modules simultaneously (on different ports)..

This is the result of a two day game-jam with two colleagues and should definitely not be used in production!


  1. Clone this repository
  2. Get all dependencies with mix deps.get
  3. Run the tests with mix test
  4. Start a server session with iex -S mix
  5. Or create a release with MIX_ENV=prod mix release


Servus sends and receives messages over socket connections. Messages must be encoded in JSON format. The format of a message is

    "type":     <REQUIRED>,
    "value":    <REQUIRED>,
    "target":   <OPTIONAL>>

The type of a message can be used in the game logic to implement different commands. The type join is special and is used to join the waiting queue for a game. The value can be any kind of data and will be passed to the receiver of the message (a game logic or server plugin). If a target is given, the message will be routed to the server plugin that that is registered under that name.

When players connect to the server by sending a join message, the player is first put into a waiting queue. Every game logic has to specify the number of players. When that number is reached a new game session is started and all the players in the queue are in game mode now. This approach is a bit unflexible since it only allows a fixed number of players per game. There is also no matching algorithm at all. Only the order in which the players join determines the opponents in a game.


The config file is config/config.exs. There are two sections: basic configuration and game specific configuration. In the base config you define which game modules and server plugins to start.

Base configuration (using the included examples)

config :servus, 
  # Start the `connect_four` backend
  backends: [:connect_four],

  # Start the Echo module (accessible to all backends)
  modules: [Echo]

Game specific configuration

config :servus,
  # Configuration for the `connect_four` backend
  # TCP and WebSocket adapters are supported
  connect_four: %{
    adapters: [
      tcp: 3334,
      web: 3335
    players_per_game: 2,
    implementation: ConnectFour

The implementation is expected to be a gen_fsm statemachine that uses Servus.Game. You should override the init/1 function and return the initial state and the data that will be passed to the fsm actions. For further information you can have a look at the example game under lib/connect_four

Server plugins (or modules)

You can write your own server plugins for various porupses, like for example, saving hiscores. A server plugin is an elixir module that uses Servus.Module. Every plugin will be started as a gen_server internally. A module has to be registered by calling the register macro as soon as possible in the module definition. There are two overridables: startup/0 and shutdown/1. startup/0 allows you to return the plugin state.

To handle messages to the plugin you use the handle macros. In it's simplest form it looks like

handle "echo", args, state do
  Logger.debug "Echo module called"

There's also a handlep macro. Use this if you don't want the module to be callable via network. handlep handlers can only be called from within the server (usually from a game backend).

This will simply echo all input. Have a look at the example plugin under lib/echo_module.

You can’t perform that action at this time.