Packages
hangman_text_client
0.1.68
0.1.87
0.1.86
0.1.85
0.1.84
0.1.83
0.1.82
0.1.81
0.1.80
0.1.79
0.1.78
0.1.77
0.1.76
0.1.75
0.1.74
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
Text client for the Hangman Game.
Current section
Files
Jump to
Current section
Files
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
alias Hangman.Text.Client.Message.{
CannotConnectToNode,
ConnectedToNode,
EngineNodeDown,
EngineNotStarted,
EnsureEngineStarted,
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 = Client.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
# E.g. `Hangman.Text.Client.start("scaffold")` on 2 client nodes.
{:error, {:already_started, _pid}} ->
GameAlreadyStarted.message(game_name, engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
# Extremely unlikely since we've already connected at this point.
{:badrpc, :nodedown} ->
EngineNodeDown.message(engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
# E.g. `iex --sname hangman_engine` without adding option `-S mix`.
{:badrpc, {:EXIT, {:undef, _}}} ->
EngineNotStarted.message(engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
# `:noproc` when client node itself is `:hangman_engine@<hostname>`
# or on the engine node after app `:hangman_engine` crashed/exited.
# To fix the issue: Application.ensure_all_started(:hangman_engine)
{:badrpc, {:EXIT, {:noproc, _}}} ->
EnsureEngineStarted.message(engine_node) |> ANSI.puts()
self() |> Process.exit(:normal)
error ->
exit(error)
end
end
end