Game Over

- 5 mins read
A game is over when there is no more than one player remaining alive. If one player is alive at the end of the game, they are shown a winner screen which directs them back to the main menu. Eliminated players are shown a Game Over screen on the round their character starts with 0 health. Even after the game is over, the game session process is still alive and the game session summary has a :game_status of “active”.
Automatic game session recovery To finish out the feature of automatically resuming games in progress during a cold start, I call the newly created resume_active_sessions function within the Application module’s start callback just after the root supervision tree is started. def start(_type, _args) do children = [ # … ] opts = [strategy: :one_for_one, name: Minotaur.Supervisor] application_start_result = Supervisor.start_link(children, opts) GameEngine.resume_active_sessions() application_start_result end Update game state every round Game state can be recovered after a full cluster crash, but the stored game state is never updated after the game session process is started.
Test-Driven Design I’m building the feature to automatically continue active game sessions that aren’t able to be restarted by the session supervisor such as when the entire cluster is shutdown. I start by writing a test for the behavior I am trying to build which will give me a focused feedback loop with which to iterate on this feature. As usual, I write an non-compiling test structured how I want the interface to behave which will guide the function design from the outside in.
Out of memory! Today, I noticed my development droplet in Digital Ocean does not have enough memory to build docker images after adding the Timex dependency to the project. The running state on the server is usually around 50% memory usage, but it spikes during builds and deploys. I have kept the resource size low to save a few bucks, but the time has finally come to upgrade the droplet to 2GB of memory.
When a user has at least one active game, they will see a Games in Progress section on their authenticated home page. This list only shows the join code string for each active game which isn’t too helpful unless the user knows how to plug in the code to the correct URL path. The next feature I’m building is to add clickable links which take the user to the associated game page.
A few tests are failing since the latest change due to the test setups using dummy User ids which are invalid for the foreign key constraint of PlayerGameSessionSummary. These tests will now need to create User records in the database during setup. Some of these modules also need to add the line use Minotaur.DataCase which will check out a database connection during setup. I apply these changes and most of the tests are passing.

Creating Summary Records

- 3 mins read
Returning to my previous test for the unfinished get_active_games_for_user function, I update the test to reflect the changes with game_id and the introduction of a join_code. describe "get_active_games_for_user/1 when user has active games" do setup [:create_user, :join_games] test "should return a list of user's active game ids", ctx do active_game_codes = [ ctx.join_code1, ctx.join_code2 ] result = GameEngine.get_active_games_for_user(ctx.user.id) assert [game_summary1, game_summary2] = result assert Enum.member?(active_game_codes, game_summary1.join_code) assert Enum.member?(active_game_codes, game_summary2.join_code) end end I update get_active_games_for_user with an Ecto query to find the records based on how I expect them to exist in the database even though there is not yet any logic for creating records.

Secondary Process Registry

- 5 mins read
Get game state by join_code I need to update how the game LiveView fetches the initial game state when the LV mounts so it uses a join_code instead of a game_id. I first update the router path slugs to use /:join_code instead of /:game_id and update the mount function to match this change. To preserve the existing behavior, I map join_code to game_id which is used elsewhere in the mount function.

Refactoring Process Identifiers

- 4 mins read
I’m thinking about where in the startup flow for game sessions to create database records which will be used to track active games. There are scenarios where existing game sessions will go through process initialization more than once such as rolling deployments where active game processes need to shut down and start up again on another node. If the database records for each game are created in the GenServer initialization, I’ll need to account for both cases of new games and existing games.

Creating Game Summary Tables

- 4 mins read
I need to implement behavior to track all games in which a user is a player. My initial idea is to have the game session process write summary updates to the database as the game progresses which includes the list of players and their status in the game. I will create a table to track game session summaries and an associated table which contains a row for each player in a game.