Cauldron2D.Distribution (Cauldron2D v0.2.2)

Copy Markdown View Source

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

Summary

Functions

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

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.

The characters a join code is made of.

A fresh join code.

How many characters a join code has.

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 the node <name>@<address> with join code code: {:ok, node} once connected, else why not —

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

Types

host()

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

Functions

address()

@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()

@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()

@spec alphabet() :: charlist()

The characters a join code is made of.

code()

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

A fresh join code.

code_length()

@spec code_length() :: pos_integer()

How many characters a join code has.

host(name, opts \\ [])

@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(name, address, code)

@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(map)

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

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