2. Game Server for Upcoming Wooga Game
What is a game server?
• provide HTTP API to actual game („client“)
• validate API calls against current state & game logic
• API calls will modify the game state
• make game state persistent
3. Game Server for Upcoming Wooga Game
It will be a stateful game server
• one process per ac4ve user gaming session
• the process holds the current state and is the only one that can modify it
(strong encapsula:on)
• the process handles all API calls for the given user one a=er the other
(concurrency control through actor model)
• the process loads the game state from storage and writes it back
periodically and on process termina:on (:meout = user stopped playing)
4. Game Server for Upcoming Wooga Game
Details on the basic idea:
Awesome presenta4on on the
Magic Land game server by
@knu4n & @hungryblank
hZp://www.slideshare.net/wooga/from-‐0-‐to-‐1000000-‐daily-‐users-‐with-‐erlang
5. Our Goals
• Get most of the benefits from wooga’s pure-‐erlang game
server (Magic Land)
• especially func:onal approach for game state
encapsula:on and transforma:on + concurrency control
• But: Keep Object-‐Oriented approach for modelling the
game logic
6. Expected OO-‐Benefits
• Rapid development
• concise & expressive syntax
• leverage exis4ng know how
• but: keep the OO part stateless and side-‐effect free
to avoid the usual traps & pidalls
7. Target Architecture
Authority for:
Load Balancer
"What app server is responsible for
current session?"
App Server Node App Server Node App Server Node
Erlang VM Erlang VM Erlang VM
...
Ruby Ruby Ruby Ruby Ruby Ruby Ruby Ruby Ruby
... ... ...
Worker Worker Worker Worker Worker Worker Worker Worker Worker
Shared Storage
for game state snapshots and for storing cold game sessions
8. Example Game Ac4on in Ruby
URL
game_action '/:actor/fruit_tree/self/shake',
:affects => [:fruit_trees, :user] do |response|
x, y = params[:x], params[:y]
fruit_trees[x, y].shake affected parts of
end
the game state
• DSL-‐like defini4on of game ac4on
• skinny as controllers should be 8-‐)
9. Example Model in Ruby
Inheritance
class FruitTree < Tree
property :last_shake_time, :type => Integer, :default => 0 DSL-‐like defini4on
property :collectable_fruit_count, :type => Integer, :default => 0
of persistent state
def shake
raise Error::Validation, "FruitTree at (#{x}, #{y}) has no fruit" unless carries_fruit?
session.user.xp += 1
session.user.energy -= 1
self.last_shake_time = game_time
self.collectable_fruit_count = config.fruit_count
end
# ...
end
• easily unit testable
• minimal amount of code
10. erlang talking to Ruby
Some op4ons
• erlectricity hZps://github.com/mojombo/erlectricity:
Can talk erlang binary protocol to Ruby processes through erlang
ports
• ernie hZps://github.com/mojombo/ernie:
Remote func4on call using the BERT-‐RPC protocol to either Ruby or
na4ve erlang processes
• ZeroMQ <hZp://www.zeromq.org/>:
awesome brokerless queue transport layer, connects 30+ languages
11. ZeroMQ
Pro Con
• loose coupling of erlang and • yet another layer
Ruby through queues
• easily deploy new Ruby
code without touching
erlang
• allows flexible transports,
even accross machines
12. Connec4ng the Dots
• use Mongrel2 hZp://mongrel2.org/ protocol & ZeroMQ setup
• erlang: emongrel2 hZps://github.com/hungryblank/emongrel2
• Ruby
• rack-‐mongrel2 fork hEps://github.com/khiltd/khi-‐rack-‐mongrel2
• rack protocol hEp://rack.rubyforge.org
• Sinatra hEp://www.sinatrarb.com/
➡ essen4ally we are speaking HTTP over ZeroMQ
and can hook up any Rack-‐based Ruby web framework
14. How does the game state look like?
• Has many parts, each part has a name (e.g. fruit_trees) and some
content
• this is a performance op:miza:on, so that we don't need to send the
complete state back and forth for every call
• erlang does not care about the content (binary data)
• actually the content contains serialized objects (e.g. a
MapObjectCollection containing FruitTree members)
• erlang does need to know, what game ac4on needs what state parts
15. Looking back at the Game Ac4on
game_action '/:actor/fruit_tree/self/shake',
:affects => [:fruit_trees, :user] do |response|
x, y = params[:x], params[:y]
fruit_trees[x, y].shake affected parts of
end
the game state
• Ruby knows the mapping of game ac4ons to affected state parts
• pushes the mapping on startup & makes it available on request
16. Aside: Efficient Game State Access
• considered different serializa4on formats (e.g. JSON,
MessagePack)
• especially for large collec4ons of objects we wanted to avoid
parsing everything to extract a single object
• chose TNetstrings hZp://tnetstrings.org/ as a serializa4on
format (length prefix helps searching/lazy parsing)
• built a lazy parser/encoder for it in C for speed :-‐)
hZps://github.com/wooga/lazy_tnetstring