Skip to content

Commit

Permalink
fix: incorrect velocity when standing still
Browse files Browse the repository at this point in the history
  • Loading branch information
akiver committed Nov 12, 2023
1 parent 02f91df commit 421329c
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 38 deletions.
51 changes: 24 additions & 27 deletions pkg/demoinfocs/common/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@ import (
type Player struct {
demoInfoProvider demoInfoProvider // provider for demo info such as tick-rate or current tick

SteamID64 uint64 // 64-bit representation of the user's Steam ID. See https://developer.valvesoftware.com/wiki/SteamID
LastAlivePosition r3.Vector // The location where the player was last alive. Should be equal to Position if the player is still alive.
UserID int // Mostly used in game-events to address this player
Name string // Steam / in-game user name
Inventory map[int]*Equipment // All weapons / equipment the player is currently carrying. See also Weapons().
AmmoLeft [32]int // Ammo left for special weapons (e.g. grenades), index corresponds Equipment.AmmoType
EntityID int // Usually the same as Entity.ID() but may be different between player death and re-spawn.
Entity st.Entity // May be nil between player-death and re-spawn
FlashDuration float32 // Blindness duration from the flashbang currently affecting the player (seconds)
FlashTick int // In-game tick at which the player was last flashed
TeamState *TeamState // When keeping the reference make sure you notice when the player changes teams
Team Team // Team identifier for the player (e.g. TeamTerrorists or TeamCounterTerrorists).
IsBot bool // True if this is a bot-entity. See also IsControllingBot and ControlledBot().
IsConnected bool
IsDefusing bool
IsPlanting bool
IsReloading bool
IsUnknown bool // Used to identify unknown/broken players. see https://github.com/markus-wa/demoinfocs-golang/issues/162
LastPositions []r3.Vector // CS2 only, used to compute velocity as it's not networked in CS2 demos
SteamID64 uint64 // 64-bit representation of the user's Steam ID. See https://developer.valvesoftware.com/wiki/SteamID
LastAlivePosition r3.Vector // The location where the player was last alive. Should be equal to Position if the player is still alive.
UserID int // Mostly used in game-events to address this player
Name string // Steam / in-game user name
Inventory map[int]*Equipment // All weapons / equipment the player is currently carrying. See also Weapons().
AmmoLeft [32]int // Ammo left for special weapons (e.g. grenades), index corresponds Equipment.AmmoType
EntityID int // Usually the same as Entity.ID() but may be different between player death and re-spawn.
Entity st.Entity // May be nil between player-death and re-spawn
FlashDuration float32 // Blindness duration from the flashbang currently affecting the player (seconds)
FlashTick int // In-game tick at which the player was last flashed
TeamState *TeamState // When keeping the reference make sure you notice when the player changes teams
Team Team // Team identifier for the player (e.g. TeamTerrorists or TeamCounterTerrorists).
IsBot bool // True if this is a bot-entity. See also IsControllingBot and ControlledBot().
IsConnected bool
IsDefusing bool
IsPlanting bool
IsReloading bool
IsUnknown bool // Used to identify unknown/broken players. see https://github.com/markus-wa/demoinfocs-golang/issues/162
PreviousFramePosition r3.Vector // CS2 only, used to compute velocity as it's not networked in CS2 demos
}

func (p *Player) PlayerPawnEntity() st.Entity {
Expand Down Expand Up @@ -522,12 +522,8 @@ func (p *Player) PositionEyes() r3.Vector {
// Velocity returns the player's velocity.
func (p *Player) Velocity() r3.Vector {
if p.demoInfoProvider.IsSource2() {
if !p.IsAlive() || len(p.LastPositions) != 2 {
return r3.Vector{}
}

t := 64.0
diff := p.LastPositions[1].Sub(p.LastPositions[0])
diff := p.Position().Sub(p.PreviousFramePosition)

return r3.Vector{
X: diff.X * t,
Expand Down Expand Up @@ -813,9 +809,10 @@ type demoInfoProvider interface {
// Intended for internal use only.
func NewPlayer(demoInfoProvider demoInfoProvider) *Player {
return &Player{
Inventory: make(map[int]*Equipment),
demoInfoProvider: demoInfoProvider,
LastAlivePosition: r3.Vector{},
Inventory: make(map[int]*Equipment),
demoInfoProvider: demoInfoProvider,
LastAlivePosition: r3.Vector{},
PreviousFramePosition: r3.Vector{},
}
}

Expand Down
12 changes: 7 additions & 5 deletions pkg/demoinfocs/common/player_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,9 +432,10 @@ func createPlayerForVelocityTest() *Player {
controllerEntity := entityWithProperties([]fakeProp{
{propName: "m_hPlayerPawn", value: st.PropertyValue{Any: uint64(1), S2: true}},
})
pawnEntity := entityWithProperties([]fakeProp{
{propName: "m_iHealth", value: st.PropertyValue{Any: int32(100), S2: true}},
})
pawnEntity := new(stfake.Entity)
position := r3.Vector{X: 20, Y: 300, Z: 100}

pawnEntity.On("Position").Return(position)

pl := &Player{
Entity: controllerEntity,
Expand All @@ -453,14 +454,15 @@ func createPlayerForVelocityTest() *Player {

func TestPlayer_VelocityS2(t *testing.T) {
pl := createPlayerForVelocityTest()
pl.LastPositions = []r3.Vector{{X: 10, Y: 200, Z: 50}, {X: 20, Y: 300, Z: 100}}
pl.PreviousFramePosition = r3.Vector{X: 10, Y: 200, Z: 50}

expected := r3.Vector{X: 640, Y: 6400, Z: 3200}
assert.Equal(t, expected, pl.Velocity())
}

func TestPlayer_VelocityS2WithoutPositions(t *testing.T) {
func TestPlayer_VelocityDidNotChangeS2(t *testing.T) {
pl := createPlayerForVelocityTest()
pl.PreviousFramePosition = r3.Vector{X: 20, Y: 300, Z: 100}

expected := r3.Vector{X: 0, Y: 0, Z: 0}
assert.Equal(t, expected, pl.Velocity())
Expand Down
6 changes: 0 additions & 6 deletions pkg/demoinfocs/datatables.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,12 +569,6 @@ func (p *parser) bindNewPlayerPawnS2(pawnEntity st.Entity) {
}
if pl.IsAlive() {
pl.LastAlivePosition = pos
pl.LastPositions = append(pl.LastPositions, pos)
if len(pl.LastPositions) > 2 {
pl.LastPositions = pl.LastPositions[1:]
}
} else {
pl.LastPositions = nil
}
})

Expand Down
4 changes: 4 additions & 0 deletions pkg/demoinfocs/parsing.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,10 @@ func (p *parser) handleFrameParsed(*frameParsedTokenType) {

p.currentFrame++
p.eventDispatcher.Dispatch(events.FrameDone{})

if p.isSource2() {
p.updatePlayersPreviousFramePosition()
}
}

// CS2 demos playback info are available in the CDemoFileInfo message that should be parsed at the end of the demo.
Expand Down
6 changes: 6 additions & 0 deletions pkg/demoinfocs/s2_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,9 @@ func (p *parser) handleDemoFileHeader(msg *msgs2.CDemoFileHeader) {
p.header.MapName = msg.GetMapName()
p.header.NetworkProtocol = int(msg.GetNetworkProtocol())
}

func (p *parser) updatePlayersPreviousFramePosition() {
for _, player := range p.GameState().Participants().Playing() {
player.PreviousFramePosition = player.Position()
}
}

0 comments on commit 421329c

Please sign in to comment.