Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Game Servers in OTP
Search
Martin Schürrer
July 26, 2014
Programming
700
9
Share
Game Servers in OTP
Given at ElixirConf 2014
Martin Schürrer
July 26, 2014
More Decks by Martin Schürrer
See All by Martin Schürrer
Deploying Elixir
msch
3
310
RailsGirls Linz Lightning Talk
msch
0
150
Erlang for Rubyists
msch
4
710
Whirlwind Tour of Erlang
msch
1
280
Other Decks in Programming
See All in Programming
Going Multiplatform with Your Android App (Android Makers 2026)
zsmb
2
430
Don't Prompt Harder, Structure Better
kitasuke
0
770
mruby on C#: From VM Implementation to Game Scripting (RubyKaigi 2026)
hadashia
2
530
tRPCの概要と少しだけパフォーマンス
misoton665
2
210
Server-Side Kotlin LT大会 vol.18 [Kotlin-lspの最新情報と Neovimのlsp設定例]
yasunori0418
1
150
「話せることがない」を乗り越える 〜日常業務から登壇テーマをつくる思考法〜
shoheimitani
4
820
PDI: Como Alavancar Sua Carreira e Seu Negócio
marcelgsantos
0
120
ルールルルルルRubyの中身の予備知識 ── RubyKaigiの前に予習しなイカ?
ydah
1
180
第3木曜LT会 #28
tinykitten
PRO
0
110
実用!Hono RPC2026
yodaka
2
230
ハーネスエンジニアリングとは?
kinopeee
10
5.4k
How We Benchmarked Quarkus: Patterns and anti-patterns
hollycummins
1
140
Featured
See All Featured
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
100
How to train your dragon (web standard)
notwaldorf
97
6.6k
Deep Space Network (abreviated)
tonyrice
0
120
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.1k
Paper Plane (Part 1)
katiecoart
PRO
0
6.7k
Believing is Seeing
oripsolob
1
110
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
We Are The Robots
honzajavorek
0
220
What's in a price? How to price your products and services
michaelherold
247
13k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
3.8k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
140
sira's awesome portfolio website redesign presentation
elsirapls
0
220
Transcript
Game Servers in OTP
@msch featurebranch.com
None
None
None
None
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
HA keepalived haproxy active Erlang active Erlang standby Erlang standby
PostgreSQL haproxy standby
Erlang active
OTP Apps :cardgame :storage :network :gameserver
:cardgame OTP APP {Table,9} {Table,8} {Table,7} {Table,6}
:network OTP APP WebSocket :cowboy Flash (TCP) :ranch
:storage OTP APP PSQLDBWorker TableIdSequence
:gameserver OTP APP {Player,1} {Player,2} {Player,3}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
None
DealCards Knocking DealCards Play Result idle playing
[ {DealCards, [cards_to_deal, :private]}, {Knocking, [5000]}, {DealCards, [cards_to_deal, :private]}, {Negotiation,
[]}, {Play, [contra_enabled: true]}, {Play, []}, {Play, []}, … {Play, []}, {Play, []}, {Result, []} ]
gen_fsm:send_event/2 -----> Module:StateName/2 gen_fsm:send_event/2 -----> Table.playing/2 {:next_state, next_state_name, new_state_data} !
{:stop, reason, new_state_data}
DealCards Knocking DealCards Play Result idle playing
{:next_stage, new_stage_data, new_state_data} ! {:end_game, new_stage_data, new_state_data} ! Table.broadcast_event {:next_state,
next_state_name, new_state_data} ! {:stop, reason, new_state_data} ! GenServer.reply Table.playing/2 -----> DealCards.playing/3
DealCards Knocking DealCards Play Result idle playing
Knocking DealCards Play Result idle playing
DealCards Play Result idle playing
Play Result idle playing {:game_event, …}
Play Result idle playing {:game_event, …}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
P1 P2 P3 P4 {Table,9} {Player,1}
P1 P2 P3 P4 {Table,9} {Player,1} Ports
P1 P2 P3 P4 {Table,9} {Player,1}
P1 P2 P3 P4 {Table,9} {Player,1} {:game_event, :must_play_card}
P1 {Table,9} {Player,1} {:game_event, …}
P1 {Table,9} {Player,1} { "type": "mustPlayCard" } {:game_event, :must_play_card} ProtocolJSON.write/1
P1 {Table,9} {Player,1}
P1 {Table,9} {Player,1} { "type": "playACard", "card": "XH" } {:game_event,
{:play_card,{:ten,:hearts}} ProtocolJSON.parse/1
P1 {Table,9} {Player,1} {:game_event, …}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
P1 P2 P3 P4 {Table,9} {Player,1}
haproxy active Erlang active Erlang standby Erlang standby
haproxy active Erlang standby Erlang active Erlang standby
:rpc.multicall(GameServer, switch_active, [node()])
P1 P2 P3 P4 {Table,9} P5 P6 P7 old
P1 P2 P3 P4 {Table,9} P5 P6 P7 old
P1 P2 P3 P4 {Table,9} old
P1 P2 P3 P4 {Table,9} old new {TableMigrator,9}
P1 P2 P3 P4 {Table,9} old new {TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
ConnectionProxy {TableMigrator,9}
P1 P2 P3 P4 {Table,9} P5 P6 P7 old new
{TableMigrator,9}
P1 P2 P3 P4 {Table,9} old new {TableMigrator,9}
None
DealCards Knocking DealCards Play Result idle playing
P1 P2 P3 P4 old new {TableMigrator,9} portable_state
P1 2 3 4 old new { portable_state portable_state %{table_id:
9, player_ids: [1,2,3,4], positions: %{1 => 3, 2 => 0, 3 => 1, 4 => 2}, modifications: [:short_hand], last_game_id: 123 }
P1 P2 P3 P4 old new {TableMigrator,9}
old new {TableMigrator,9}
old new {TableMigrator,9}
None
None
old new {TableMigrator,9}
old new {TableMigrator,9} P1
old new {TableMigrator,9} P2 P3 P4 P1
old new {TableMigrator,9} P2 P3 P4 P1 portable_state
old new P2 P3 P4 P1 {Table,9}
None
P2 P3 P4 P1 {Table,9}
Architecture big-picture The Table-FSM Life of an in-game event Real
world concerns BoldPoker <3 Elixir Lessons Learned & QA
All-in! Elixir Rewrite
Why? Extensibility goals stdlib mix ExUnit
:gen_fsm :ets GenServer Supervisor alias :ets, as: ETS
proper Tests send_events(ClientDeviceIDs, ClientIP) -> ?FORALL(Event, event(), ?FORALL(Data, event_data(Event), ...
parse_transform ?
Architecture big-picture The Table-FSM Life of an in-game event Moving
tables across servers BoldPoker <3 Elixir Lessons Learned & QA
Perform en/decoding at the system border P1 { "type": "playACard",
"card": "XH" } {:game_event, {:play_card,{:ten,:hearts}} ProtocolJSON.parse/1 WS
Create your own behaviours for fun better reusability and profit
easier testing {:next_stage, new_stage_data, new_state_data} {:end_game, new_stage_data, new_state_data}
Distributed Erlang makes hard things easy ConnectionProxy
DB failure? We got all data already, so finish the
game, then just store result on disk
Thank you! @msch featurebranch.com