allejo / postgame

A Symfony 4 application to summarize BZFlag replay files and perform analysis on them
https://postgame.allejo.org/
MIT License
1 stars 3 forks source link

Add functionality to create a Heatmap from Replay files #32

Closed Adwitiya-Singh closed 3 years ago

allejo commented 3 years ago

Alrighty! Let's get this PR incorporated into Postgame core. First: rebase on top of latest develop.

Set Up Postgame

I had you build this separate from Postgame first so we could delay getting Postgame set up properly on your computer. I do not have a development container setup, which means you will need to set up an application like XAMPP and start both the Apache and MariaDB servers.

Setup an .env.local file at the root of Postgame and make sure it has a DATABASE_URL value. For example, on my local machine, the file contains:

DATABASE_URL=mysql://root:root@127.0.0.1:8889/bzflag-postgame

Where bzflag-postgame is the name of the database I created (you'll need to do this either via phpMyAdmin or php bin/console doctrine:database:create)

After the database is created, you will need to run our database migrations to create our schema.

php bin/console doctrine:migrations:migrate

Once this is set up, now you can import the sample replay file I sent you.

bin/console app:replay:import /path/to/replay-i-sent-you.rec

If you go to your local website, you'll see something similar to this:

image

Because you're writing code to actually import a replay, you'll need to delete the replay over and over again as you work on this feature. Get the ID of the replay you just imported and delete.

php bin/console app:replay:delete <id>

Merging this into core

This functionality will be located in our ReplayImportService (src/Service/ReplayImportService.php). This is a complicated service that handles everything about reading in a replay.

Take a look ReplayImportService::handlePacket(). You will see I have an if statement that calls respective methods dedicated to certain packet types. You will need to add your own if condition to this and watch for packets of MsgPlayerUpdate instance; i.e. create a handleMsgPlayerUpdate() method.

You do not need to migrate your listening to MsgAddPlayer and MsgRemovePlayer, this service already does that. And... replays can be a bit... messed up where packets for players that haven't fully joined yet are already sent. This service already takes into account all of that. Use ReplayImportService::currPlayersByIndex in this service instead of your id_to_callsign map.

Create ReplayImportService::$currPlayersHeatMap private array that will store callsigns to PlayerMovementGrid objects.

Create a PlayerMovementGrid (src/Utility/) class. In each call to handleMsgPlayerUpdate(), you will check if the callsign already exists in $currPlayersHeatMap, if not, create a new PlayerMovementGrid.

In this method, you will then store the player locations in the respective PlayerMovementGrid objects (i.e. call an addPosition method).

Create a new entity called PlayerHeatMap (run php bin/console make:entity) that should contain the following information:

Step through the interactive wizard of creating the entity and all of the fields. This will automatically create a migration file for you, a much better process than Laravel imo.

In ReplayImportService::importReplay, add and call a new method called processHeatMaps() before the call to updateBatchStatus. In this new method you're going to be converting PlayerMovementGrid instances into PlayerHeatMap instances and persisting them to our ORM.

https://github.com/allejo/postgame/blob/50482cc82339e15fc4ef19ea6ff2d87a3993b53f/src/Service/ReplayImportService.php#L298

Write a new HeatMapWriterService and this is where your SVG writing logic will be placed. Take a look at MapThumbnailWriterService (src/Service/MapThumbnailWriterService.php) for an idea of how they look.

Use dependency injection to inject an instance of HeatMapWriterService into our ReplayImportService (i.e. look at the constructor and see how it works).

In the processHeatMaps method, you will now call a new method from HeatMapWriterService called writeHeatMap(Replay $replay, PlayerHeatMap $heatMap).

This writer will write to a folder called "heat-maps" and the filename will be in the format of <replay_id>-<player_callsign>.svg (where <player_callsign> is URL encoded; urlencode).


Anyway, this is why I wanted you to write this separately from Postgame core at first lol