Current section

Files

Jump to
hangman_text_client lib hangman text client engine.ex
Raw

lib/hangman/text/client/engine.ex

defmodule Hangman.Text.Client.Engine do
@moduledoc """
Starts a game locally or remotely:
- locally when local node is not alive (`:nonode@nohost`)
- remotely on node `:hangman_engine@<hostname>` otherwise
"""
use PersistConfig
alias IO.ANSI.Plus, as: ANSI
alias Hangman.{Engine, Game}
alias Hangman.Text.Client.Message.{
CannotConnectToNode,
ConnectedToNode,
EngineNodeDown,
EngineNotStarted,
GameAlreadyStarted
}
@doc """
Starts locally or remotely a _Hangman Game_ named `game_name`.
"""
@spec new_game(Game.name()) :: Game.name() | no_return
def new_game(game_name), do: node() |> new_game(game_name)
## Private functions
@spec new_game(node, Game.name()) :: Game.name() | no_return
defp new_game(:nonode@nohost = _local_node, game_name) do
{:ok, _apps} = Application.ensure_all_started(:hangman_engine)
{:ok, _pid} = Engine.new_game(game_name)
game_name
end
defp new_game(_local_node, game_name) do
engine_node = engine_node()
if Node.connect(engine_node) do
ConnectedToNode.message(engine_node) |> ANSI.puts()
else
CannotConnectToNode.message(engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
end
# Synchronizes the global name server with all nodes known to this node.
:ok = :global.sync()
# Remote procedure call to call a function on a remote node.
case :rpc.call(engine_node, Engine, :new_game, [game_name]) do
{:ok, _pid} ->
game_name
{:error, {:already_started, _pid}} ->
GameAlreadyStarted.message(game_name, engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
{:badrpc, :nodedown} ->
EngineNodeDown.message(engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
{:badrpc, {:EXIT, {:undef, _}}} ->
EngineNotStarted.message(engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
error ->
exit(error)
end
end
@spec engine_node :: node
defp engine_node, do: get_env(:engine_node)
end