Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
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
9
690
Game Servers in OTP
Given at ElixirConf 2014
Martin Schürrer
July 26, 2014
Tweet
Share
More Decks by Martin Schürrer
See All by Martin Schürrer
Deploying Elixir
msch
3
300
RailsGirls Linz Lightning Talk
msch
0
140
Erlang for Rubyists
msch
4
690
Whirlwind Tour of Erlang
msch
1
270
Other Decks in Programming
See All in Programming
sbt 2
xuwei_k
0
220
LLMで複雑な検索条件アセットから脱却する!! 生成的検索インタフェースの設計論
po3rin
1
430
新卒エンジニアのプルリクエスト with AI駆動
fukunaga2025
0
180
STYLE
koic
0
100
テストやOSS開発に役立つSetup PHP Action
matsuo_atsushi
0
140
堅牢なフロントエンドテスト基盤を構築するために行った取り組み
shogo4131
8
2.1k
WebRTC と Rust と8K 60fps
tnoho
2
1.9k
Navigation 3: 적응형 UI를 위한 앱 탐색
fornewid
1
190
ハイパーメディア駆動アプリケーションとIslandアーキテクチャ: htmxによるWebアプリケーション開発と動的UIの局所的適用
nowaki28
0
360
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
syumai
5
1.9k
AIコーディングエージェント(Manus)
kondai24
0
150
dotfiles 式年遷宮 令和最新版
masawada
1
690
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.3k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Why Our Code Smells
bkeepers
PRO
340
57k
Build The Right Thing And Hit Your Dates
maggiecrowley
38
3k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
9
1k
Being A Developer After 40
akosma
91
590k
GraphQLとの向き合い方2022年版
quramy
50
14k
Become a Pro
speakerdeck
PRO
30
5.7k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
196
69k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.5k
Building Adaptive Systems
keathley
44
2.9k
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