Home Posts Post Search Tag Search

PokerLive - 01 - Create and Join Room
Published on: 2026-03-27 Tags: Side Project, Agents, Game Site, Poker, GenServer, Player, Room

Okay so this will be a series of posts about my work to build a Multiplayer Poke Site.


For this first one we will look at GenServer for the Room context and the Struct for the Player context.


For this we need to have a room GenServer that will allow use to keep track of Players and the state of the room that we are currently in.

defmodule GameSite.MultiPoker.Room do
  use GenServer

  alias GameSite.MultiPoker.Player

  defstruct players: %{},
            room_id: nil,
            room_status: :waiting,
            host_id: nil,
            full: false

  def new(%Player{} = host, opts \\ []) do
    host_id = host.player_id
    room_id = Keyword.get(opts, :room_id)
    room_status = Keyword.get(opts, :room_status, :waiting)
    full = Keyword.get(opts, :full, false)

    %__MODULE__{
      players: %{host_id => host},
      room_id: room_id,
      room_status: room_status,
      host_id: host_id,
      full: full
    }
  end

  def add_player(pid, %Player{} = player) do
    GenServer.cast(pid, {:add_player, player})
  end

  def remove_player(pid, %Player{} = player) do
    GenServer.cast(pid, {:remove_player, player})
  end

  def update_status(pid, status) do
    GenServer.cast(pid, {:update_status, status})
  end

  def get_state(pid) do
    GenServer.call(pid, :get_room_state)
  end

  def start_link(host, opts \\ []) do
    GenServer.start_link(__MODULE__, {host, opts})
  end

  @impl true
  def init({host, opts}) do
    initial_state = new(host, opts)
    {:ok, initial_state}
  end

  @impl true
  def handle_cast({:add_player, %Player{} = player}, %__MODULE__{players: players} = state) do
    new_players = Map.put(players, player.player_id, player)
    {:noreply, %__MODULE__{state | players: new_players}}
  end

  @impl true
  def handle_cast({:update_status, status}, state) do
    {:noreply, %__MODULE__{state | room_status: status}}
  end

  @impl true
  def handle_cast({:remove_player, %Player{} = player}, %__MODULE__{players: players} = state) do
    new_players = Map.delete(players, player.player_id)
    {:noreply, %__MODULE__{state | players: new_players}}
  end

  @impl true
  def handle_cast({:full_room}, state) do
    {:noreply, %__MODULE__{state | full: true}}
  end

  @impl true
  def handle_cast({:has_room}, state) do
    {:noreply, %__MODULE__{state | full: false}}
  end
end

Next we need to build the Player context that will allow us to update a Player. This will keep track of things like :current_bet, :chips:, :folded, and more.

defmodule GameSite.MultiPoker.Player do
  defstruct player_id: nil,
            ready?: false,
            chips: 1000,
            current_bet: 0,
            folded?: false,
            seat_position: nil,
            hand: [],
            connected?: true

  def new(id, opts \\ []) do
    ready = Keyword.get(opts, :ready?, false)
    chips = Keyword.get(opts, :chips, 1000)
    current_bet = Keyword.get(opts, :current_bet, 0)
    folded = Keyword.get(opts, :folded?, false)
    seat_position = Keyword.get(opts, :seat_position, nil)
    hand = Keyword.get(opts, :hand, [])
    connected = Keyword.get(opts, :connected?, true)

    %__MODULE__{
      player_id: id,
      ready?: ready,
      chips: chips,
      current_bet: current_bet,
      folded?: folded,
      seat_position: seat_position,
      hand: hand,
      connected?: connected
    }
  end

  def change(%__MODULE__{} = player, opts \\ []) do
    updates = Enum.into(opts, %{})
    struct(player, updates)
  end

  def set_ready(%__MODULE__{} = player, ready) do
    %__MODULE__{player | ready?: ready}
  end
end

This will be enough to get us started I will add in the _Supervisor Tree on the next post.