From 11db275db24fa7b3d0fe24e8d2d0b3c5a5606b2f Mon Sep 17 00:00:00 2001 From: Kodi Craft Date: Sun, 18 Aug 2024 13:02:48 +0200 Subject: [PATCH] Add GameCoordinator logic for finding or creating games --- apps/amethyst/lib/amethyst.ex | 3 +- apps/amethyst/lib/api/game.ex | 16 +++--- apps/amethyst/lib/apps/game_coordinator.ex | 61 ++++++++++++++++------ 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/apps/amethyst/lib/amethyst.ex b/apps/amethyst/lib/amethyst.ex index bde95b6..cb4c1b1 100644 --- a/apps/amethyst/lib/amethyst.ex +++ b/apps/amethyst/lib/amethyst.ex @@ -26,7 +26,8 @@ defmodule Amethyst.Application do children = [ {Task.Supervisor, name: Amethyst.ConnectionSupervisor}, {Amethyst.Keys, 1024}, - {Amethyst.GameRegistry, []} + {Amethyst.GameRegistry, []}, + {Amethyst.GameCoordinator, []} ] children = case Application.fetch_env!(:amethyst, :port) do diff --git a/apps/amethyst/lib/api/game.ex b/apps/amethyst/lib/api/game.ex index b2e1f59..2723dfe 100644 --- a/apps/amethyst/lib/api/game.ex +++ b/apps/amethyst/lib/api/game.ex @@ -12,18 +12,18 @@ defmodule Amethyst.API.Game do def child_spec(state) do %{ id: __MODULE__, - start: {__MODULE__, :start_link, []} + start: {__MODULE__, :register_self, []} } end - def start_link() do - register_self() - _loop() - end - defp _loop() do - _loop() - end def register_self() do Amethyst.GameRegistry.register(__MODULE__, unquote(meta)) + Process.sleep(:infinity) + end + def start(state) do + loop(state) + end + defp loop(state) do + loop(state) end end end diff --git a/apps/amethyst/lib/apps/game_coordinator.ex b/apps/amethyst/lib/apps/game_coordinator.ex index b71ee44..c915402 100644 --- a/apps/amethyst/lib/apps/game_coordinator.ex +++ b/apps/amethyst/lib/apps/game_coordinator.ex @@ -6,31 +6,62 @@ defmodule Amethyst.GameCoordinator do instances of each game and can create new ones on demand. """ + def start_link(initial) when is_list(initial) do + GenServer.start_link(__MODULE__, initial, name: {:global, __MODULE__}) + end + @impl true def init(initial) do {:ok, initial} end @impl true - def handle_call({:get, type}, _from, state) do - {:reply, find_or_create(type, state), state} + def handle_call({:find, type}, _from, state) do + {pid, state} = _find(type, state) + {:reply, pid, state} end - defp create(mod) do - state = mod.initialize() - spawn(mod, :listen, [state]) + @impl true + def handle_call({:create, type}, _from, state) do + {pid, state} = _create(type, state) + {:reply, pid, state} end - defp find(type, games) do - existing = games |> Enum.filter(fn {mod, _pid, _opts} -> mod == type end) - [{_mod, pid, _} | _] = existing - pid - end - - defp find_or_create(type, games) do - case find(type, games) do - nil -> create(type) - some -> some + @impl true + def handle_call({:find_or_create, type}, from, state) do + {pid, state} = _find(type, state) + case pid do + nil -> handle_call({:create, type}, from, state) + some -> {:reply, some, state} end end + + defp _create(type, games) do + state = type.instantiate() + pid = spawn(type, :start, [state]) + games = [{type, pid, []} | games] + {pid, games} + end + + defp _find(type, games) do + alive_games = games |> Enum.filter(fn {_mod, pid, _opts} -> Process.alive?(pid) end) + # TODO: Here we should have some additional filtering for specifically joinable games + existing = alive_games |> Enum.filter(fn {mod, _pid, _opts} -> mod == type end) + if length(existing) > 0 do + [{_mod, pid, _} | _] = existing + {pid, alive_games} + else + {nil, alive_games} + end + end + + def create(type) do + GenServer.call({:global, __MODULE__}, {:create, type}) + end + def find(type) do + GenServer.call({:global, __MODULE__}, {:find, type}) + end + def find_or_create(type) do + GenServer.call({:global, __MODULE__}, {:find_or_create, type}) + end end