Skip to content

Commit

Permalink
Hopefully fix lingering games
Browse files Browse the repository at this point in the history
  • Loading branch information
simmsb committed Oct 9, 2023
1 parent 7e84e24 commit af3c74b
Show file tree
Hide file tree
Showing 21 changed files with 283 additions and 571 deletions.
2 changes: 1 addition & 1 deletion .dir-locals.el
Original file line number Diff line number Diff line change
@@ -1 +1 @@
((elixir-mode . ((lsp-elixir-project-dir . "web/"))))
((elixir-ts-mode . ((lsp-elixir-project-dir . "web/"))))
3 changes: 2 additions & 1 deletion web/.formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@
consumes: 1
],
import_deps: [:phoenix, :typed_struct],
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"],
plugins: [Phoenix.LiveView.HTMLFormatter]
]
2 changes: 2 additions & 0 deletions web/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ priv/static/
priv/native/

native/matrix/target/

.direnv/
56 changes: 38 additions & 18 deletions web/flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions web/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
description = "Dev deps for stuff";

inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
fenix.url = "github:nix-community/fenix";
};
Expand All @@ -18,10 +19,11 @@
nodejs
yarn
rustComponents
beam.packages.erlangR25.elixir_1_14
beam.packages.erlang.rebar3
beam.packages.erlangR25.hex
erlangR25
beam.packages.erlang.hex
beam.packages.erlang.erlang
beam.packages.erlang.elixir
elixir-ls
git
];
in
Expand Down
43 changes: 26 additions & 17 deletions web/lib/infolab_light_games/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,39 @@ defmodule InfolabLightGames.Application do
use Application

def start(_type, _args) do
children = [
# Start the Telemetry supervisor
InfolabLightGamesWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: InfolabLightGames.PubSub},
# Start the Endpoint (http/https)
Presence,
InfolabLightGamesWeb.Endpoint,
# Start a worker by calling: InfolabLightGames.Worker.start_link(arg)
# {InfolabLightGames.Worker, arg}
GameSupervisor,
Screen,
Coordinator,
Bans,
MatrixPow,
Scheduler
]
children =
[
# Start the Telemetry supervisor
InfolabLightGamesWeb.Telemetry,
# Start the PubSub system
{Phoenix.PubSub, name: InfolabLightGames.PubSub},
# Start the Endpoint (http/https)
Presence,
InfolabLightGamesWeb.Endpoint
# Start a worker by calling: InfolabLightGames.Worker.start_link(arg)
# {InfolabLightGames.Worker, arg}
] ++
if in_phoenix?(),
do: [
GameSupervisor,
Screen,
Coordinator,
Bans,
MatrixPow,
Scheduler
],
else: []

# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: InfolabLightGames.Supervisor]
Supervisor.start_link(children, opts)
end

defp in_phoenix?() do
Application.get_env(:phoenix, :serve_endpoints)
end

# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
def config_change(changed, _new, removed) do
Expand Down
99 changes: 70 additions & 29 deletions web/lib/infolab_light_games/coordinator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,33 @@ defmodule Coordinator do
end

@impl true
def handle_cast({:terminate, id}, state) do
GenServer.stop(via_tuple(id))
def handle_info({:force_kill_game, id}, %State{} = state) do
try_stop(via_tuple(id))

state = handle_terminated_game(id, state)
{:noreply, handle_terminated_game(id, state)}
end

@impl true
def handle_info({:force_kill_idle_animation, pid}, %State{} = state) do
try_stop(pid)

{:noreply, state}
end

@impl true
def handle_cast(:terminate_idle_animation, %State{} = state) do
if !is_nil(state.current_idle_animation) and GenServer.whereis(state.current_idle_animation) do
GenServer.stop(state.current_idle_animation)
end
def handle_cast({:idle_animation_terminated, id}, %State{} = state) do
state =
if state.current_idle_animation == via_tuple(id) do
%State{state | current_idle_animation: nil}
else
state
end

{:noreply, state}
{:noreply, state, {:continue, :tick}}
end

def handle_cast({:terminated, id}, state) do
@impl true
def handle_cast({:game_terminated, id}, %State{} = state) do
state = handle_terminated_game(id, state)

Phoenix.PubSub.broadcast!(
Expand All @@ -66,17 +75,33 @@ defmodule Coordinator do
end

@impl true
def handle_cast({:terminated_idle_animation, id}, %State{} = state) do
Logger.info("Idle animation quit #{id}")
def handle_cast({:terminate, id}, state) do
try do
pid = via_tuple(id)
GenServer.cast(pid, :terminate)

state =
if state.current_idle_animation == via_tuple(id) do
%State{state | current_idle_animation: nil}
else
state
Process.send_after(self(), {:force_kill_game, id}, 5_000)
catch
_ -> :ok
end

{:noreply, state}
end

@impl true
def handle_cast(:terminate_idle_animation, %State{} = state) do
if !is_nil(state.current_idle_animation) do
try do
pid = state.current_idle_animation
GenServer.cast(pid, :terminate)

Process.send_after(self(), {:force_kill_idle_animation, pid}, 5_000)
catch
_ -> :ok
end
end

{:noreply, state, {:continue, :tick}}
{:noreply, state}
end

@impl true
Expand All @@ -94,7 +119,16 @@ defmodule Coordinator do

opts = meta ++ [game_id: id, name: via_tuple(id)]

{:ok, _pid} = DynamicSupervisor.start_child(GameManager, {game, opts})
{:ok, pid} = DynamicSupervisor.start_child(GameManager, {game, opts})

Task.start_link(fn ->
ref = Process.monitor(pid)

receive do
{:DOWN, ^ref, :process, ^pid, _reason} ->
GenServer.cast(__MODULE__, {:game_terminated, id})
end
end)

:ok = GenServer.call(via_tuple(id), {:add_player, initial_player})

Expand Down Expand Up @@ -184,7 +218,7 @@ defmodule Coordinator do
# stop, otherwise if there's no idle animation we can start the game
if state.current_idle_animation do
Logger.info("Requesting idle animation quits")
GenServer.cast(state.current_idle_animation, :terminate)
terminate_idle_animation()
else
GenServer.call(state.current_game, :start_if_ready)
end
Expand All @@ -205,9 +239,9 @@ defmodule Coordinator do
defp start_idle_animation(%State{} = state, module, mode) do
Logger.info("Starting idle animation #{module}:#{inspect(mode)}")
# stop the idle animation if it exists
if !is_nil(state.current_idle_animation) and GenServer.whereis(state.current_idle_animation) do
if !is_nil(state.current_idle_animation) do
# we need the idle animation to stop immediately so it doesn't try to draw over us
GenServer.stop(state.current_idle_animation)
try_stop(state.current_idle_animation)
end

id = random_id()
Expand All @@ -218,6 +252,15 @@ defmodule Coordinator do
{module, game_id: id, name: via_tuple(id), mode: mode}
)

Task.start_link(fn ->
ref = Process.monitor(pid)

receive do
{:DOWN, ^ref, :process, ^pid, _reason} ->
GenServer.cast(__MODULE__, {:idle_animation_terminated, id})
end
end)

{%State{state | current_idle_animation: via_tuple(id)}, pid}
end

Expand Down Expand Up @@ -300,6 +343,12 @@ defmodule Coordinator do
{:via, Registry, {GameRegistry, id}}
end

defp try_stop(pid) do
if GenServer.whereis(pid) do
GenServer.stop(pid)
end
end

def terminate_game(id) do
Logger.info("terminating game #{id}")
GenServer.cast(__MODULE__, {:terminate, id})
Expand All @@ -309,14 +358,6 @@ defmodule Coordinator do
GenServer.cast(__MODULE__, :terminate_idle_animation)
end

def notify_game_terminated(id) do
GenServer.cast(__MODULE__, {:terminated, id})
end

def notify_idle_animation_terminated(id) do
GenServer.cast(__MODULE__, {:terminated_idle_animation, id})
end

def route_input(player, input) do
GenServer.cast(__MODULE__, {:route_input, player, input})
end
Expand Down
5 changes: 0 additions & 5 deletions web/lib/infolab_light_games/games/pong.ex
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,6 @@ defmodule Games.Pong do
end
end

@impl true
def terminate(_reason, state) do
Coordinator.notify_game_terminated(state.id)
end

defp tick(state) do
{dx, dy} = state.ball_vel

Expand Down
5 changes: 0 additions & 5 deletions web/lib/infolab_light_games/games/snake.ex
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,6 @@ defmodule Games.Snake do
end
end

@impl true
def terminate(_reason, state) do
Coordinator.notify_game_terminated(state.id)
end

defp tick(%State{} = state) do
state =
state
Expand Down
Loading

0 comments on commit af3c74b

Please sign in to comment.