When a small startup of experienced game developers in Seattle finished their first game, an open-world single player zombie survival game, to rave reviews they continuously received the same feedback - "No multiplayer?". Building the necessary software infrastructure for a massively multiplayer online game historically requires large engineering teams, even larger operational support organizations, and can take years to finish. With a big success under their belts the organization had a choice to make: Go through a massive growth period, potentially ruining the culture they worked so hard to cultivate or hire smart, work even smarter, and use the right tools for the job. In this talk you will experience this story from to back. Come hear how Undead Labs grew from a small 20 person game development shop creating a single player Xbox 360 Live Arcade Game, State of Decay, to a multi-game studio bringing State of Decay and more to the online space. You will hear how we leveraged Elixir, the OTP ecosystem, and DevOps to create the foundations of Undead's online platform while simultaneously developing multiple games and the personnel required for the studio's future.
87. Protocol'Message'Defini1on
defmodule TUProtocol.Chat do
use TUProtocol.Behaviour, protocol_id: 1
protocol_message "WhisperSend" do
field :sender_id, :integer
field :receiver_name, :string
field :receiver_tag, :integer
field :content, :string
end
# ... other definitions
end
91. Listener
defmodule Route.Listener do
alias Route.Config
def start do
:ranch.start_listener(__MODULE__, Config.acceptors,
:ranch_tcp, [port: Config.port], Route.ConnHandler, [])
end
end
Ranch&Documenta-on:
h"p://ninenines.eu/docs/en/ranch/HEAD/guide/introduc7on/
98. Message&Dispatcher
defmodule Chat.Dispatcher do
alias Protocol.Chat.Message, as: ChatMsg
use Net.Dispatcher
def handle_message(%ChatMsg.Login{} = request) do
case Chat.User.login(request) do
:ok ->
{:reply, NetError.success}
{:error, :banned} ->
{:reply, NetError.banned("ch:login:1")}
end
end
# ... other handle_message/1 clauses
end
110. Tubes&Net
defmodule Net do
@spec call(atom, integer | binary, atom, atom, list)
def call(protocol, route_key, module, function, arguments) do
# ...
end
@spec cast(atom, integer | binary, atom, atom, list)
def cast(protocol, route_key, module, function, arguments) do
# ...
end
end
111. defmodule Chat.User do
alias Protocol.Chat.Message, as: ChatMsg
def whisper(sender_id, receiver_id, body) do
Net.cast(Protocol.Chat, receiver_id,
__MODULE__, :rpc_whisper, [receiver_id, sender_id, body])
end
def rpc_whisper(receiver, sender, body) do
case lookup(receiver) do
{:ok, acceptor} ->
msg = %ChatMsg{receiver: receiver, sender: sender, body: body}
client_send(acceptor, msg)
_ -> :ok
end
end
end
113. Embedded&Shard&/&Create&Date&In&En11es
CREATE SEQUENCE next_id_seq;
CREATE OR REPLACE FUNCTION next_id(OUT result bigint) AS $$
DECLARE
our_epoch bigint := 1409266191000;
seq_id bigint;
now_millis bigint;
shard_id int := 1;
BEGIN
SELECT nextval('next_id_seq') % 1024 INTO seq_id;
SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
result := (now_millis - our_epoch) << 23;
result := result | (seq_id << 13);
result := result | (shard_id);
END;
115. Extract'Shard'ID
def extract(id) when is_integer(id) do
extract(<<id::size(64)>>)
end
def extract(<<_::size(51), shard_id::size(13)>>) do
shard_id
end
117. Hash/Mod)Shard)Value
defmodule Tubes.Shard do
use Bitwise
@divisor 128
def hash(value) when is_binary(value) do
String.downcase(value) |> Fnv1a.hash
end
def mod(value) when is_integer(value) do
(value &&& (@divisor - 1))
end
end
118. defmodule Fnv1a do
use Bitwise
@offset 2166136261
@prime 16777619
def hash(term) when is_binary(term) do
_hash(@offset, 0, term)
end
def hash(term) do
:erlang.term_to_binary(term) |> hash
end
defp _hash(hash, byte_offset, bin) when byte_size(bin) == byte_offset, do: hash
defp _hash(hash, byte_offset, bin) do
<<_::size(byte_offset)-binary, octet::size(8), _::binary>> = bin
xord = hash ^^^ octet
hash = rem((xord * @prime), (2 <<< 31))
_hash(hash, byte_offset + 1, bin)
end
end