Skip to content

Commit f88e6d5

Browse files
committed
fix: incorrect velocity when standing still
1 parent 02f91df commit f88e6d5

File tree

5 files changed

+40
-38
lines changed

5 files changed

+40
-38
lines changed

pkg/demoinfocs/common/player.go

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@ import (
1515
type Player struct {
1616
demoInfoProvider demoInfoProvider // provider for demo info such as tick-rate or current tick
1717

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

3939
func (p *Player) PlayerPawnEntity() st.Entity {
@@ -522,12 +522,8 @@ func (p *Player) PositionEyes() r3.Vector {
522522
// Velocity returns the player's velocity.
523523
func (p *Player) Velocity() r3.Vector {
524524
if p.demoInfoProvider.IsSource2() {
525-
if !p.IsAlive() || len(p.LastPositions) != 2 {
526-
return r3.Vector{}
527-
}
528-
529525
t := 64.0
530-
diff := p.LastPositions[1].Sub(p.LastPositions[0])
526+
diff := p.Position().Sub(p.PreviousFramePosition)
531527

532528
return r3.Vector{
533529
X: diff.X * t,
@@ -813,9 +809,9 @@ type demoInfoProvider interface {
813809
// Intended for internal use only.
814810
func NewPlayer(demoInfoProvider demoInfoProvider) *Player {
815811
return &Player{
816-
Inventory: make(map[int]*Equipment),
817-
demoInfoProvider: demoInfoProvider,
818-
LastAlivePosition: r3.Vector{},
812+
Inventory: make(map[int]*Equipment),
813+
demoInfoProvider: demoInfoProvider,
814+
PreviousFramePosition: r3.Vector{},
819815
}
820816
}
821817

pkg/demoinfocs/common/player_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,10 @@ func createPlayerForVelocityTest() *Player {
432432
controllerEntity := entityWithProperties([]fakeProp{
433433
{propName: "m_hPlayerPawn", value: st.PropertyValue{Any: uint64(1), S2: true}},
434434
})
435-
pawnEntity := entityWithProperties([]fakeProp{
436-
{propName: "m_iHealth", value: st.PropertyValue{Any: int32(100), S2: true}},
437-
})
435+
pawnEntity := new(stfake.Entity)
436+
position := r3.Vector{X: 20, Y: 300, Z: 100}
437+
438+
pawnEntity.On("Position").Return(position)
438439

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

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

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

462-
func TestPlayer_VelocityS2WithoutPositions(t *testing.T) {
463+
func TestPlayer_VelocityDidNotChangeS2(t *testing.T) {
463464
pl := createPlayerForVelocityTest()
465+
pl.PreviousFramePosition = r3.Vector{X: 20, Y: 300, Z: 100}
464466

465467
expected := r3.Vector{X: 0, Y: 0, Z: 0}
466468
assert.Equal(t, expected, pl.Velocity())

pkg/demoinfocs/datatables.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -569,12 +569,6 @@ func (p *parser) bindNewPlayerPawnS2(pawnEntity st.Entity) {
569569
}
570570
if pl.IsAlive() {
571571
pl.LastAlivePosition = pos
572-
pl.LastPositions = append(pl.LastPositions, pos)
573-
if len(pl.LastPositions) > 2 {
574-
pl.LastPositions = pl.LastPositions[1:]
575-
}
576-
} else {
577-
pl.LastPositions = nil
578572
}
579573
})
580574

pkg/demoinfocs/parsing.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,10 @@ func (p *parser) handleFrameParsed(*frameParsedTokenType) {
525525

526526
p.currentFrame++
527527
p.eventDispatcher.Dispatch(events.FrameDone{})
528+
529+
if p.isSource2() {
530+
p.updatePlayersPreviousFramePosition()
531+
}
528532
}
529533

530534
// CS2 demos playback info are available in the CDemoFileInfo message that should be parsed at the end of the demo.

pkg/demoinfocs/s2_commands.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,3 +378,9 @@ func (p *parser) handleDemoFileHeader(msg *msgs2.CDemoFileHeader) {
378378
p.header.MapName = msg.GetMapName()
379379
p.header.NetworkProtocol = int(msg.GetNetworkProtocol())
380380
}
381+
382+
func (p *parser) updatePlayersPreviousFramePosition() {
383+
for _, player := range p.GameState().Participants().Playing() {
384+
player.PreviousFramePosition = player.Position()
385+
}
386+
}

0 commit comments

Comments
 (0)