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
711 stars 95 forks source link

Property 'm_iHealth' not found. Disconnected players are not properly handled. #551

Open gregor256 opened 4 months ago

gregor256 commented 4 months ago

Description Hi, I've tried to parse a demo from match containing disconnected player. The code just gets the health of every player on every game tick. I've got panic error property 'm_iHealth' not found. I provide my ideas at the end of issue.

Stacktrace > 2024/07/08 22:51:59 failed to parse demo: property 'm_iHealth' not found stacktrace: goroutine 18 [running]: runtime/debug.Stack() /usr/local/go/src/runtime/debug/stack.go:24 +0x5e github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs.NewParserWithConfig.func1({0x6f77e0, 0xc002fe8a00}) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/parser.go:403 +0x2a github.com/markus-wa/godispatch.(*Dispatcher).dispatchWithRecover.func1() /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:197 +0x3b panic({0x6f77e0?, 0xc002fe8a00?}) /usr/local/go/src/runtime/panic.go:770 +0x132 github.com/markus-wa/godispatch.callConsumerCode.func1() /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:122 +0x54 panic({0x6f77e0?, 0xc002fe89f0?}) /usr/local/go/src/runtime/panic.go:770 +0x132 github.com/markus-wa/godispatch.callConsumerCode.func1() /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:122 +0x54 panic({0x69f800?, 0xc002fe89e0?}) /usr/local/go/src/runtime/panic.go:770 +0x132 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/sendtables2.(*Entity).PropertyValueMust(0x3?, {0x7ad272, 0x9}) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/sendtables2/entity.go:170 +0x134 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/common.getInt(...) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/common/entity_util.go:10 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/common.(*Player).Health(0xc0009136c0) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/common/player.go:373 +0x129 main.testExtractReplays.func1({0x0?, 0x466913?}) /home/gregor/issue/example.go:29 +0x56 reflect.Value.call({0x6a0500?, 0xc00014e360?, 0x0?}, {0x7abdcb, 0x4}, {0xc0005a5178, 0x1, 0x6b7600?}) /usr/local/go/src/reflect/value.go:596 +0xca6 reflect.Value.Call({0x6a0500?, 0xc00014e360?, 0x40f822?}, {0xc0005a5178?, 0x10?, 0x780000c0005a5108?}) /usr/local/go/src/reflect/value.go:380 +0xb9 github.com/markus-wa/godispatch.callConsumerCode({0x6a0500?, 0xc00014e360?, 0xc0005a5190?}, {0xc0005a5178?, 0x713fef2455e8?, 0xc0004854e0?}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:125 +0x3f github.com/markus-wa/godispatch.(*Dispatcher).Dispatch(0xc0000ca300, {0x6e19c0, 0xc0009136c0}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:111 +0x20c github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs.(*parser).bindNewPlayerControllerS2.func1({{0x0, 0x0, 0x0}, 0x0, 0x0, {0x0, 0x0, 0x0}, {0x7fb848, 0x0}, ...}) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/datatables.go:587 +0xf2 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/sendtables2.(*Entity).readFields(0xc0003e8ee0, 0xc0030a31d0, 0xc0005a55f0) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/sendtables2/entity.go:454 +0x319 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/sendtables2.(*Parser).OnPacketEntities(0xc0000f0280, 0xc000b1a360) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/sendtables2/entity.go:562 +0x68c reflect.Value.call({0x6abde0?, 0xc0000d6438?, 0x0?}, {0x7abdcb, 0x4}, {0xc0005a5ef0, 0x1, 0xdea19000?}) /usr/local/go/src/reflect/value.go:596 +0xca6 reflect.Value.Call({0x6abde0?, 0xc0000d6438?, 0x40f822?}, {0xc0005a5ef0?, 0x21f66fd6105?, 0x6e00000000000000?}) /usr/local/go/src/reflect/value.go:380 +0xb9 github.com/markus-wa/godispatch.callConsumerCode({0x6abde0?, 0xc0000d6438?, 0xc0005a5f08?}, {0xc0005a5ef0?, 0xc0005a5ec0?, 0xc0005a5ec0?}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:125 +0x3f github.com/markus-wa/godispatch.(*Dispatcher).Dispatch(0xc0000ca2a0, {0x798a80, 0xc000b1a360}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:111 +0x20c github.com/markus-wa/godispatch.(*Dispatcher).dispatchWithRecover(0xc0000ca2b8?, {0x798a80?, 0xc000b1a360?}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:202 +0x4f github.com/markus-wa/godispatch.(*Dispatcher).dispatchQueue(0xc0000ca2a0, 0xc0000ca360) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:177 +0xd0 created by github.com/markus-wa/godispatch.(*Dispatcher).AddQueues in goroutine 1 /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:162 +0x18a panic: failed to parse demo: property 'm_iHealth' not found stacktrace: goroutine 18 [running]: runtime/debug.Stack() /usr/local/go/src/runtime/debug/stack.go:24 +0x5e github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs.NewParserWithConfig.func1({0x6f77e0, 0xc002fe8a00}) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/parser.go:403 +0x2a github.com/markus-wa/godispatch.(*Dispatcher).dispatchWithRecover.func1() /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:197 +0x3b panic({0x6f77e0?, 0xc002fe8a00?}) /usr/local/go/src/runtime/panic.go:770 +0x132 github.com/markus-wa/godispatch.callConsumerCode.func1() /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:122 +0x54 panic({0x6f77e0?, 0xc002fe89f0?}) /usr/local/go/src/runtime/panic.go:770 +0x132 github.com/markus-wa/godispatch.callConsumerCode.func1() /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:122 +0x54 panic({0x69f800?, 0xc002fe89e0?}) /usr/local/go/src/runtime/panic.go:770 +0x132 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/sendtables2.(*Entity).PropertyValueMust(0x3?, {0x7ad272, 0x9}) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/sendtables2/entity.go:170 +0x134 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/common.getInt(...) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/common/entity_util.go:10 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/common.(*Player).Health(0xc0009136c0) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/common/player.go:373 +0x129 main.testExtractReplays.func1({0x0?, 0x466913?}) /home/gregor/issue/example.go:29 +0x56 reflect.Value.call({0x6a0500?, 0xc00014e360?, 0x0?}, {0x7abdcb, 0x4}, {0xc0005a5178, 0x1, 0x6b7600?}) /usr/local/go/src/reflect/value.go:596 +0xca6 reflect.Value.Call({0x6a0500?, 0xc00014e360?, 0x40f822?}, {0xc0005a5178?, 0x10?, 0x780000c0005a5108?}) /usr/local/go/src/reflect/value.go:380 +0xb9 github.com/markus-wa/godispatch.callConsumerCode({0x6a0500?, 0xc00014e360?, 0xc0005a5190?}, {0xc0005a5178?, 0x713fef2455e8?, 0xc0004854e0?}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:125 +0x3f github.com/markus-wa/godispatch.(*Dispatcher).Dispatch(0xc0000ca300, {0x6e19c0, 0xc0009136c0}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:111 +0x20c github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs.(*parser).bindNewPlayerControllerS2.func1({{0x0, 0x0, 0x0}, 0x0, 0x0, {0x0, 0x0, 0x0}, {0x7fb848, 0x0}, ...}) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/datatables.go:587 +0xf2 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/sendtables2.(*Entity).readFields(0xc0003e8ee0, 0xc0030a31d0, 0xc0005a55f0) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/sendtables2/entity.go:454 +0x319 github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs/sendtables2.(*Parser).OnPacketEntities(0xc0000f0280, 0xc000b1a360) /home/gregor/go/pkg/mod/github.com/markus-wa/demoinfocs-golang/v4@v4.2.3/pkg/demoinfocs/sendtables2/entity.go:562 +0x68c reflect.Value.call({0x6abde0?, 0xc0000d6438?, 0x0?}, {0x7abdcb, 0x4}, {0xc0005a5ef0, 0x1, 0xdea19000?}) /usr/local/go/src/reflect/value.go:596 +0xca6 reflect.Value.Call({0x6abde0?, 0xc0000d6438?, 0x40f822?}, {0xc0005a5ef0?, 0x21f66fd6105?, 0x6e00000000000000?}) /usr/local/go/src/reflect/value.go:380 +0xb9 github.com/markus-wa/godispatch.callConsumerCode({0x6abde0?, 0xc0000d6438?, 0xc0005a5f08?}, {0xc0005a5ef0?, 0xc0005a5ec0?, 0xc0005a5ec0?}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:125 +0x3f github.com/markus-wa/godispatch.(*Dispatcher).Dispatch(0xc0000ca2a0, {0x798a80, 0xc000b1a360}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:111 +0x20c github.com/markus-wa/godispatch.(*Dispatcher).dispatchWithRecover(0xc0000ca2b8?, {0x798a80?, 0xc000b1a360?}) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:202 +0x4f github.com/markus-wa/godispatch.(*Dispatcher).dispatchQueue(0xc0000ca2a0, 0xc0000ca360) /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:177 +0xd0 created by github.com/markus-wa/godispatch.(*Dispatcher).AddQueues in goroutine 1 /home/gregor/go/pkg/mod/github.com/markus-wa/godispatch@v1.4.1/dispatch.go:162 +0x18a goroutine 1 [running]: log.Panicf({0x7b2710?, 0x6a0500?}, {0xc0018caf10?, 0x0?, 0x0?}) /usr/local/go/src/log/log.go:439 +0x65 main.testExtractReplays() /home/gregor/issue/example.go:37 +0x1ed main.main() /home/gregor/issue/example.go:12 +0xf exit status 2

To Reproduce To Reproduce you may download demo: disconnected_de_anubis.dem and run the following code.

Code:

package main

import (
    "fmt"
    "log"
    "os"

    dem "github.com/markus-wa/demoinfocs-golang/v4/pkg/demoinfocs"
)

func main() {
    testExtractReplays()
}

func testExtractReplays() error {
    reader, _ := os.Open("disconnected_de_anubis.dem")
    parser := dem.NewParser(reader)
    defer parser.Close()
    var dummy int
    parser.RegisterEventHandler(func(any) {
        gs := parser.GameState()
        for _, player := range gs.Participants().Playing() {
            dummy = player.Health()
        }
    })
    err := parser.ParseToEnd()
    if err != nil {
        if err != dem.ErrInvalidFileType {
            log.Panicf("failed to parse demo: %v", err)
        }
    }
    fmt.Println(dummy)
    return nil
}

Expected behavior Clear parsing to end without errors printing 0 as last player health.

Library version v4.2.3

My solution Main problem is that user with steamId = 76561198886630731 is listed in participants. He is disconnected, but all methods are tried to be applied to him. But his "entity" doesn't contain needed properties. This patch #489 does not solve the problem because this player has property p.Entity.PropertyValue("m_hPawn"). This checking does not find that he is disconnected.

For me helped adding this checking:

parser.RegisterEventHandler(func(any) {
    gs := parser.GameState()
    for _, player := range gs.Participants().Playing() {
        if player == nil || player.PlayerPawnEntity() == nil ||
            player.PlayerPawnEntity().ServerClass().Name() != "CCSPlayerPawn" {
            continue
        }
        dummy = player.Health()
    }
})

Maybe this checking should be made in all places mentioned in #489.

markus-wa commented 3 weeks ago

Yeah we should definitely add these checks