Monstrofil / replays_unpack

51 stars 19 forks source link

Exporting player position as json #3

Closed imkindaprogrammermyself closed 4 years ago

imkindaprogrammermyself commented 4 years ago

Can you add the code for the animated gif as an example?

Monstrofil commented 4 years ago

Unfortunately, that code was removed right after gif was created (that was just a matplotlib graph rendered as .png for each position change + ffmpeg to combine all that png into single .gif).

There is an example of WOT replays player in project sources and you can handle entity position change here: https://github.com/Monstrofil/replays_unpack/blob/master/examples/wot_parser/player.py#L112

You can do same parser for wows using wot example as reference (recomended way), or just fork this project and handle position change here: https://github.com/Monstrofil/replays_unpack/blob/master/replay_unpack/clients/wows/player.py#L102

imkindaprogrammermyself commented 4 years ago

Alright, I managed to add player position to the dump. Anyway, what's the maximum x and z on a map?

Monstrofil commented 4 years ago

Depends on map. Map consists of chunks, each is 0..100x0..100, so if I remember correctly, you can calculate it as sqrt(number of *.chunk files in spaces/SPACE_NAME) * 100.

Monstrofil commented 4 years ago

Oh, I found something:


        settings_path = os.path.join(self.__spaces_path, space_name, 'space.settings')
        with open(settings_path, 'rb') as f:
            tree = etree.parse(f)

        space_bounds, = tree.xpath('/space.settings/bounds')
        if space_bounds.attrib:
            minX = int(space_bounds.get('minX'))
            minY = int(space_bounds.get('minY'))
            maxX = int(space_bounds.get('maxX'))
            maxY = int(space_bounds.get('maxY'))
        else:
            minX = int(space_bounds.xpath('minX/text()')[0])
            minY = int(space_bounds.xpath('minY/text()')[0])
            maxX = int(space_bounds.xpath('maxX/text()')[0])
            maxY = int(space_bounds.xpath('maxY/text()')[0])

        chunk_size_elements = tree.xpath('/space.settings/chunkSize')
        if chunk_size_elements:
            chunk_size = float(chunk_size_elements[0].text)
        else:
            chunk_size = 100.0

        return dict(
            spaceSizeX=len(xrange(minX, maxX + 1)) * chunk_size,
            spaceSizeY=len(xrange(minY, maxY + 1)) * chunk_size,
            mapSizeX=len(xrange(minX, maxX + 1)) * chunk_size - 4 * chunk_size,
            mapSizeY=len(xrange(minY, maxY + 1)) * chunk_size - 4 * chunk_size
        )```
Monstrofil commented 4 years ago
mapSizeX=len(xrange(minX, maxX + 1)) * chunk_size - 4 * chunk_size,
mapSizeY=len(xrange(minY, maxY + 1)) * chunk_size - 4 * chunk_size

This strange math ^^ needed because wows uses extra chunk each side "outside" the world.

imkindaprogrammermyself commented 4 years ago

I added this code inside the BattleController class that will be called if a Position packet is processed

def process_position(self,  entity_id: int):
        ent = self._entities[entity_id]
        client = ent.properties["client"]
        owner = client["owner"]
        if owner not in self._movements:
            self._movements[client["owner"]] = [(ent.position.x, ent.position.y, ent.position.z)]
        else:
            self._movements[client["owner"]].append((ent.position.x, ent.position.y, ent.position.z))

I noticed self._movements length is only 23 after all the packets are processed. The player/owner of the replay isn't included in that list.

Monstrofil commented 4 years ago

I guess this PR may help: https://github.com/Monstrofil/replays_unpack/pull/1 https://github.com/Monstrofil/replays_unpack/pull/1/files#diff-ffa52a5cbf1afd02ee7ae0d63f281ccb

It has a strange logic of linking positions between two entities and I had no time to investigate why and how it works:

                if packet.data.entityId2 != (0,):
                    # This is a link packet
                    self._bigworld.entities[packet.data.entityId1[0]].position = self._bigworld.entities[packet.data.entityId2[0]].position
                elif packet.data.entityId1 != (0,) and packet.data.entityId2 == (0,):
                    self._bigworld.entities[packet.data.entityId1[0]].position = packet.data.position
                    self._bigworld.entities[packet.data.entityId1[0]].rotation = packet.data.rotation
DeckerCHAN commented 4 years ago

The logic of linking position is because you may continue view the game after your ship is sunk. So that game keep sync ship position from ship to player. When you have been sunk and start watching your team mates, it sync position from the ship you are watching.

KeithDeRuiter commented 3 years ago

So @Monstrofil what's the status of integrating that pull request? Or possibly getting a PR made for the player packet code made by @m-freiberg in his Fork since PR #1 is a few years old, but I see that the wows example folder is missing over there. The existing PR does remove a bunch of code for old versions as well, I don't know if that is a cause for hesitation or not? I don't know if there's a suitable workaround in the meantime or not (I haven't found one yet?), but I feel like the player position is relatively important.

Monstrofil commented 3 years ago

@KeithDeRuiter well, I think I can add PlayerPosition support quite fast, it is not implemented just because DeckerCHAN and imkindaprogrammermyself were the only persons who asked about that in a years and I don't need that because my primary parser usage is battle statistics report for https://replayswows.com/.

Kinda same situation with Camera position/direction data... I know where to get it, but don't need that information for my purposes.

Open another issue pls, I'll try to find some time to make couple commits.