# `Cauldron2D.Net.Session`
[🔗](https://github.com/jaman/cauldron/blob/v0.1.3/cauldron_2d_net/lib/cauldron_2d/net/session.ex#L1)

A browser player's life in a world, as functions over a struct, for a channel process
to drive: the process calling `join/4` becomes the world's player and gets its frames
and its audio's PCM as messages.

    {:ok, session, pushes} = Session.join(MyGame.Client, world, "alice", %{settings: %{sfx: 0.8, music: 0.3}})
    {session, pushes} = Session.frame(session, frame_message)
    session = Session.input(session, ["left", "act"], [10.5, 4.0], %{"act" => 0.5})

Every function that has something for the browser returns pushes, `{event, payload}`
pairs in the order to send them; a payload that is a binary is a `"pcm"` chunk.

What a session's sound costs the server is `Cauldron2D.Net.Audio`'s policy: its own
stage for effects and music, a stage for effects with the world's shared music
(`Cauldron2D.Net.Audio.Shared`) mixed under them, or none. The chunks reach the
calling process as `{:pcm, chunk}` from its own stage and `{:cauldron_shared_pcm,
chunk}` from the shared one; `pcm/2` and `shared_pcm/2` turn them into pushes, and
`rerate/2` answers `{:cauldron_audio_rate, rate}` from the policy.

# `push`

```elixir
@type push() :: {String.t(), map() | binary()}
```

# `t`

```elixir
@type t() :: %Cauldron2D.Net.Session{
  actions: %{required(String.t()) =&gt; atom()},
  audio: pid() | nil,
  cue: term(),
  format: Cauldron2D.Net.Audio.format(),
  game: module(),
  levels: %{sfx: float(), music: float()},
  local_world: pid() | nil,
  map_sent?: boolean(),
  music: :own | :static | :shared | :off,
  music_on?: boolean(),
  name: String.t(),
  queued: :queue.queue(binary()),
  shared: pid() | nil,
  sheet: Cauldron2D.Net.Sheet.t(),
  sheet_url: String.t(),
  world: GenServer.server()
}
```

# `atlas_changed`

```elixir
@spec atlas_changed(t()) :: {t(), [push()]}
```

A `{:cauldron_atlas, name, :changed}` for the game's atlas: the new sheet, and the map goes again with the next frame.

# `frame`

```elixir
@spec frame(t(), %{view: term(), events: list()}) :: {t(), [push()]}
```

A `{:cauldron_frame, message}` from the world: the map first if not yet sent, then the frame; events and cues reach the audio.

# `input`

```elixir
@spec input(t(), [String.t()], [number()] | nil, %{required(String.t()) =&gt; number()}) ::
  t()
```

What the browser holds and aims at, and how hard it holds what an analog control
holds (`strength`, action name to 0.0–1.0); actions the game does not know, and
strengths for actions not held or out of range, are dropped.

# `join`

```elixir
@spec join(module(), GenServer.server(), String.t(), map(), keyword()) ::
  {:ok, t(), [push()]} | {:error, term()}
```

Join `world` as `name` from the calling process; `game` implements
`Cauldron2D.Client.Game`. A `world` of `{module, opts}` is this player's alone,
started here and stopped by `leave/1`.

`props` carries `:settings` (`:sfx` and `:music` levels, default
`Cauldron2D.Audio.default_levels/0`) and
anything the game's `join_props/1` adds. Options: `:sheet_url`, where the game serves
the sheet PNG (default `"/atlas.png"`); `:sink`, a sink for the audio instead of
`TuningFork.Sink.Process` to this process; `:audio`, the PCM format asked for as
`%{rate: hertz, channels: 1 | 2}` (default 44 100 Hz stereo — 22 050 Hz mono is a
quarter of the bytes) or `:off` for no sound; `:policy`, a sound policy instead of
`Cauldron2D.Net.Audio.policy/0`. The pushes are the sheet, then `"audio"` with the
format the PCM comes in, or `%{off: true}` when none will. The process is subscribed
to the game's atlas; `atlas_changed/1` answers the message.

# `leave`

```elixir
@spec leave(t()) :: :ok
```

Leave the world and stop the audio.

# `levels`

```elixir
@spec levels(t(), number(), number()) :: t()
```

Set the effects and music levels, 0.0 to 1.0; shared music is scaled by the session's music level as it is mixed.

# `music`

```elixir
@spec music(t(), boolean()) :: t()
```

Switch the music off, or back on at the session's music level, leaving the effects alone; the switch outlives a change of levels.

# `pcm`

```elixir
@spec pcm(t(), binary()) :: {t(), [push()]}
```

A chunk from the session's own stage, `{:pcm, chunk}`: the push, with the world's shared music under it when the music is shared.

# `rerate`

```elixir
@spec rerate(t(), pos_integer()) :: {t(), [push()]}
```

The policy's rate changed, `{:cauldron_audio_rate, rate}`: the session's audio starts again at the new rate, and the browser is told the format.

# `shared_pcm`

```elixir
@spec shared_pcm(t(), binary()) :: {t(), [push()]}
```

A chunk of the world's shared music, `{:cauldron_shared_pcm, chunk}`: pushed as it is when the session has no stage of its own, else kept for the next chunk of its own to go under.

# `toggle_pause`

```elixir
@spec toggle_pause(t()) :: t()
```

Pause or resume the player's own world; nothing for a shared one.

---

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