markus-wa / demoinfocs-golang

A Counter-Strike 2 & CS:GO demo parser for Go (demoinfo)
https://pkg.go.dev/github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs?tab=doc
MIT License
687 stars 93 forks source link

Duplicate UniqueID/UniqueID2 For Tracking Grenades Over Time #361

Open David-Durst opened 1 year ago

David-Durst commented 1 year ago

Describe the bug UniqueID and UniqueID2 aren't unique for different grenades.

I want to use e.Projectile.WeaponInstance.UniqueID2() in events.GrenadeProjectileThrow and e.Grenade.UniqueID2 in events.HeExplode (and similar events) to track grenades over time. Here is my full code: https://github.com/David-Durst/csknow/blob/4e115f7314cb23eb3ba6d4764611731ffe568c3e/demo_parser/internal/parse_tick_data.go#L196-L308

To Reproduce https://drive.google.com/file/d/1Qv7MoXoEIrA5leIfpzmOk1mRBdtDeqZQ/view?usp=sharing

Code:

    lastID := ulid.Make()
    p.RegisterEventHandler(func(e events.GrenadeProjectileThrow) {
        if lastID == e.Projectile.WeaponInstance.UniqueID2() {
            fmt.Printf("duplicate unique ids")
        }
        lastID = e.Projectile.WeaponInstance.UniqueID2()
    })

Expected behavior "duplicate unique ids" should never print.

Instead, there are duplicate ids, so they print out.

Library version v3.0.2-0.20220908193112-50f55785b7a0

Additional context Ubuntu 22.04

David-Durst commented 1 year ago

Duplicate id's happen quite frequently (seems like 5-10% of grenades). In the old version of my parser, I kept a list of duplicate ids and assumed that grenades exploded in the same they are launched. This worked reasonably well, as it seems that most of the duplicates were flashes, and flashes have a fixed fuse length. However, that approach is janky. I would prefer if unique ids were actually unique.

David-Durst commented 1 year ago

Here's another demo https://drive.google.com/file/d/1ajifwQNd0BPPg9CXl038NfNrjrOmwTr4/view?usp=sharing. It's much newer (so no demo file structure issues), but it has the same problem. In particular, see IngameTick 35082. Niko's first flash explodes just after he throws another flash. These flashes exist simultaneously but the parser assigns them the same UniqueID/UniqueID2. This is checked both with version v3.0.2-0.20220908193112-50f55785b7a0 and v2.13.0 (the old version of the library I used until my refactor this past week).

markus-wa commented 1 year ago

interesting picture when looking at ticks

image

seems like there's always 2 events on those ticks - it's probably actually the same entity and we just get some dupe events

markus-wa commented 1 year ago

seems to always be flashbangs image

I think this is because players can have 2 flashbangs in the inventory, and we just get the item from the player inventory by type

David-Durst commented 1 year ago

I'm not seeing it on the same tick. I'm filtering out those duplicate events on same tick and still seeing the duplciate id issue.

The flashbang thing makes sense. I've only seen it when players throw flashes quickly one after another.

David-Durst commented 1 year ago

Which demo are you using?

markus-wa commented 1 year ago

right - I remember this now the tick doesn't matter actually

having 2 flashbangs counts as 1 weapon

think of it as a gun that can shoot flashbangs, and you have 2 ammo

so it makes sense that the two unique IDs are the same, because the flashbangs are coming from the same "stash"

David-Durst commented 1 year ago

Is there a way to make uniqueid actually unique? like create a new one for each "flashbang bullet"?

markus-wa commented 1 year ago

What's the underlying goal here? this seems like a XY problem

markus-wa commented 1 year ago

can you use Projectile.UniqueID()?

might need to add a V2 there as well

markus-wa commented 1 year ago

e.g. https://github.com/markus-wa/demoinfocs-golang/blob/master/examples/nade-trajectories/nade_trajectories.go#L69

David-Durst commented 1 year ago

The goal is to record the ticks when a grenade is thrown, destroyed, it's effect is active, and it's effect expires. I also want to link the grenade to it's trajectory.

David-Durst commented 1 year ago

There certainly are other approaches (like matching the trajectory starting points of the grenades), but the cleanest for me would be using Grenade.UniqueId(), so hoping there's a solution using that.

markus-wa commented 1 year ago

I see - that makes sense and I agree currently there is no nice solution to this.

I think this is probably quite tricky to implement, but I'm certainly open for suggestions on how to do this,

I can think about it for a bit as well but tbh I'm stretched very thin right now and my priorities are currently elsewhere, so I may be a bit slow on this.

David-Durst commented 1 year ago

Understood, thanks for your help!

Previously, I solved this by assuming that flashes had a constant fuse length. This enabled me to deduplciate the ids by assuming explosion order = throw order. Do you know if flashes always have a fixed fuse length? Or does bouncing/throw mode affect fuse length? If the fuse length is constant, I could go back to this approach in my code.

markus-wa commented 1 year ago

I think the order is certainly guaranteed between duplicates as they come from the same thrower - and there's no way to make your second flash explode before your first one.

As for fuse length I would have expected it to be constant, but can neither confirm nor deny this for certain.

David-Durst commented 1 year ago

https://davidbdurst.com/blog/csknow_flashbang_length.html fuse length is constant. I'll use this hack if nothing better comes up.