# `Cauldron2D.Distribution`
[🔗](https://github.com/jaman/cauldron/blob/v0.2.2/cauldron_2d/lib/cauldron_2d/distribution.ex#L1)

Play across machines over BEAM distribution: one machine hosts, the other joins its
worlds (`Cauldron2D.Worlds.world_name/2`).

    {:ok, host} = Cauldron2D.Distribution.host("nidhox", game: "NidhOX")
    host.address
    host.code

    {:ok, node} = Cauldron2D.Distribution.join("nidhox", "192.168.1.5", "K7QMW2ZD")

`host/2` names this VM `<name>@cauldron.host`, a name tied to none of the machine's
addresses, gives it a fresh join code (`code_length/0` characters of `alphabet/0`) as
its cookie, and calls on the local network (`Cauldron2D.Beacon`) with the node's name
and `address`, this machine's first address (`addresses/0`). The code never goes over
the air: the host shows it, and whoever joins types it.

`join/3` names this VM too when it is not yet distributed, points the host's name at
the address given, in this VM's own host table (looked up before the system's), and
connects to the host's node with the code as the cookie for that node alone. Any of
the host's addresses joins it — its home network's, a VPN's, or `127.0.0.1` from the
same machine.

Rules a caller must respect:

  * a connected node can run any code in this VM, so the join code is to be shown
    only to the player joining, and a host stopped (`stop/1`) once play is over
  * distribution traffic is not encrypted; play across the internet needs the host's
    ports `4369` (the node name service) and the `:port` given to `host/2` open to
    the joining machine; without `:port` the host listens on any free port
  * a VM that is already distributed hosts under its own name, with its cookie as
    the code, and `stop/1` leaves its distribution running

# `host`

```elixir
@type host() :: %{
  node: node(),
  address: String.t(),
  code: String.t(),
  beacon: pid(),
  started?: boolean()
}
```

# `address`

```elixir
@spec address() :: {:ok, String.t()} | {:error, :no_network}
```

This machine's IPv4 address on its local network, the first of `addresses/0`;
`{:error, :no_network}` when it has none.

# `addresses`

```elixir
@spec addresses() :: [String.t()]
```

Every IPv4 address of this machine's interfaces that are up, loopback aside: private
network addresses before others, and within each, those of physical interfaces
(named `en…`, `eth…` or `wl…`) before virtual ones such as a VPN's or a virtual
machine's bridge. A machine on several networks is reached at the one its peer shares.

# `alphabet`

```elixir
@spec alphabet() :: charlist()
```

The characters a join code is made of.

# `code`

```elixir
@spec code() :: String.t()
```

A fresh join code.

# `code_length`

```elixir
@spec code_length() :: pos_integer()
```

How many characters a join code has.

# `host`

```elixir
@spec host(String.t(), keyword()) :: {:ok, host()} | {:error, term()}
```

Host as `name`: this VM distributed as `<name>@cauldron.host` with a fresh join code, and
calling on the local network as `:game` (default `name`). `:port` is the port other
nodes connect on (default any free port). The beacon is linked to the caller.

# `join`

```elixir
@spec join(String.t(), String.t(), String.t()) :: {:ok, node()} | {:error, term()}
```

Join the node `<name>@<address>` with join code `code`: `{:ok, node}` once connected,
else why not —

  * `{:error, :bad_address}` — `address` is not an IPv4 address
  * `{:error, :unreachable}` — nothing answers at `address` (its port 4369, where
    nodes are looked up, is closed or blocked)
  * `{:error, :not_hosting}` — the machine answers, but no node `name` runs there
  * `{:error, :refused}` — the node runs, but the connection fails: the code is not
    its code, or its own port is blocked
  * `{:error, :no_network}` and `Node.start/2`'s errors — this machine cannot be a node

# `stop`

```elixir
@spec stop(host()) :: :ok
```

Stop hosting: no more calls on the network, and the distribution `host/2` started stopped.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
