Packages
islands_engine
0.2.54
0.2.65
0.2.64
0.2.63
0.2.62
0.2.61
0.2.60
0.2.59
0.2.58
0.2.56
0.2.55
0.2.54
0.2.53
0.2.52
0.2.51
0.2.50
0.2.49
0.2.48
0.2.47
0.2.46
0.2.45
0.2.44
0.2.43
0.2.42
0.2.41
0.2.40
0.2.39
0.2.38
0.2.37
0.2.36
0.2.35
0.2.34
0.2.33
0.2.32
0.2.31
0.2.30
0.2.29
0.2.28
0.2.27
0.2.25
0.2.24
0.2.23
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.13
0.2.12
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.81
0.1.80
0.1.79
0.1.78
0.1.77
0.1.76
0.1.75
0.1.73
0.1.72
0.1.71
0.1.70
0.1.69
0.1.68
0.1.67
0.1.66
0.1.65
0.1.64
0.1.63
0.1.62
0.1.61
0.1.60
0.1.59
0.1.58
0.1.57
0.1.56
0.1.55
0.1.54
0.1.53
0.1.52
0.1.51
0.1.50
0.1.49
0.1.48
0.1.47
0.1.46
0.1.45
0.1.44
0.1.43
0.1.42
0.1.41
0.1.40
0.1.39
0.1.38
0.1.37
0.1.36
0.1.35
0.1.34
0.1.33
0.1.32
0.1.31
0.1.30
0.1.29
0.1.28
0.1.27
0.1.26
0.1.25
0.1.24
0.1.23
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.0
Models the Game of Islands.
Current section
Files
Jump to
Current section
Files
lib/islands/engine/game_server.ex
defmodule Islands.Engine.GameServer do
@moduledoc """
A server process that holds a `game` struct as its state.
Times out after 30 minutes of inactivity.
"""
use GenServer, restart: :transient
use PersistConfig
alias __MODULE__
alias __MODULE__.{
AddPlayer,
GuessCoord,
PositionAllIslands,
PositionIsland,
ReplyTuple,
SetIslands,
Stop
}
alias Islands.Engine.Log
alias Islands.{Game, Player, Request}
@ets get_env(:ets_name)
# @reg get_env(:registry)
@timeout :timer.minutes(30)
@wait 50
@doc """
Spawns a game server process to be registered via a game name.
"""
@spec start_link({Game.name(), Player.name(), Player.gender(), pid}) ::
GenServer.on_start()
def start_link({game_name, player1_name, gender, pid} = _init_arg) do
GenServer.start_link(
GameServer,
{game_name, player1_name, gender, pid},
name: via(game_name)
)
end
# @spec via(Game.name()) :: {:via, module, {atom, tuple}}
# def via(game_name), do: {:via, Registry, {@reg, key(game_name)}}
@doc """
Allows to register or look up a game server process via `game_name`.
"""
@spec via(Game.name()) :: {:global, tuple}
def via(game_name), do: {:global, key(game_name)}
@doc """
Takes a backup of `game` and returns it.
"""
@spec save(Game.t()) :: Game.t()
def save(game) do
:ok = Log.info(:save, {game, __ENV__})
true = :ets.insert(@ets, {key(game.name), game})
game
end
## Private functions
@spec key(Game.name()) :: tuple
defp key(game_name), do: {GameServer, game_name}
@spec game(Game.name(), Player.name(), Player.gender(), pid) :: Game.t()
defp game(game_name, player1_name, gender, pid) do
case :ets.lookup(@ets, key(game_name)) do
[] ->
:ok = Log.info(:spawned, {game_name, player1_name, __ENV__})
Game.new(game_name, player1_name, gender, pid) |> save()
[{_key, game}] ->
:ok = Log.info(:restarted, {game_name, player1_name, __ENV__})
game
end
end
## Callbacks
@spec init({Game.name(), Player.name(), Player.gender(), pid}) ::
{:ok, Game.t(), timeout}
def init({game_name, player1_name, gender, pid} = _init_arg),
do: {:ok, game(game_name, player1_name, gender, pid), @timeout}
@spec handle_call(Request.t(), GenServer.from(), Game.t()) :: ReplyTuple.t()
def handle_call({:add_player, _, _, _} = request, from, game),
do: AddPlayer.handle_call(request, from, game)
def handle_call({:position_island, _, _, _, _} = request, from, game),
do: PositionIsland.handle_call(request, from, game)
def handle_call({:position_all_islands, _} = request, from, game),
do: PositionAllIslands.handle_call(request, from, game)
def handle_call({:stop, _} = request, from, game),
do: Stop.handle_call(request, from, game)
def handle_call({:set_islands, _} = request, from, game),
do: SetIslands.handle_call(request, from, game)
def handle_call({:guess_coord, _, _, _} = request, from, game),
do: GuessCoord.handle_call(request, from, game)
def handle_call({:tally, player_id}, _from, game),
do: ReplyTuple.new(game, player_id)
@spec handle_info(term, Game.t()) ::
{:stop, reason :: tuple, Game.t()} | {:noreply, Game.t()}
def handle_info(:timeout, game) do
:ok = Log.info(:timeout, {@timeout, game, __ENV__})
{:stop, {:shutdown, :timeout}, game}
end
def handle_info(_message, game), do: {:noreply, game}
@spec terminate(term, Game.t()) :: :ok
def terminate(:shutdown = reason, game) do
:ok = Log.info(:terminate, {reason, game, __ENV__})
true = :ets.delete(@ets, key(game.name))
# Ensure message logged before exiting...
Process.sleep(@wait)
end
def terminate(reason, game) do
:ok = Log.error(:terminate, {reason, game, __ENV__})
true = :ets.delete(@ets, key(game.name))
# Ensure message logged before exiting...
Process.sleep(@wait)
end
end