Skip to content

Commit e4a59f2

Browse files
committed
fix Player.IsBlinded() inaccuracies
1 parent ecb9302 commit e4a59f2

File tree

7 files changed

+67
-18
lines changed

7 files changed

+67
-18
lines changed

common/player.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,36 @@ type Player struct {
3030
ViewDirectionX float32
3131
ViewDirectionY float32
3232
FlashDuration float32 // Blindness duration from the flashbang currently affecting the player (seconds)
33+
FlashTick int // In-game tick at which the player was last flashed
3334
Team Team
3435
TeamState *TeamState // When keeping the reference make sure you notice when the player changes teams
3536
IsBot bool
3637
IsDucking bool
3738
IsDefusing bool
3839
HasDefuseKit bool
3940
HasHelmet bool
41+
42+
tickRate float64 // the in-game tick rate, used for IsBlinded()
43+
ingameTickProvider ingameTickProvider // provider for the current in-game tick, used for IsBlinded()
4044
}
4145

4246
// IsAlive returns true if the Hp of the player are > 0.
4347
func (p *Player) IsAlive() bool {
4448
return p.Hp > 0
4549
}
4650

47-
// IsBlinded returns true if the player is currently flashed (FlashDuration > 0).
51+
// IsBlinded returns true if the player is currently flashed.
52+
// This is more accurate than 'FlashDuration != 0' as it takes into account the FlashTick and GameState.IngameTick().
4853
func (p *Player) IsBlinded() bool {
49-
return p.FlashDuration > 0
54+
return p.FlashDuration > float32(p.ingameTickProvider()-p.FlashTick)/float32(p.tickRate)
5055
}
5156

5257
// FlashDurationTime returns the duration of the blinding effect as time.Duration instead of float32 in seconds.
58+
// Will return 0 if IsBlinded() returns false.
5359
func (p *Player) FlashDurationTime() time.Duration {
60+
if !p.IsBlinded() {
61+
return time.Duration(0)
62+
}
5463
return time.Duration(float32(time.Second) * p.FlashDuration)
5564
}
5665

@@ -95,9 +104,16 @@ type AdditionalPlayerInformation struct {
95104
TotalCashSpent int
96105
}
97106

107+
// ingameTickProvider is a function that returns the current ingame tick of the demo related to a player.
108+
type ingameTickProvider func() int
109+
98110
// NewPlayer creates a *Player with an initialized equipment map.
99111
//
100112
// Intended for internal use only.
101-
func NewPlayer() *Player {
102-
return &Player{RawWeapons: make(map[int]*Equipment)}
113+
func NewPlayer(tickRate float64, ingameTickProvider ingameTickProvider) *Player {
114+
return &Player{
115+
RawWeapons: make(map[int]*Equipment),
116+
tickRate: tickRate,
117+
ingameTickProvider: ingameTickProvider,
118+
}
103119
}

common/player_test.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@ func TestPlayerActiveWeapon(t *testing.T) {
1212
glock := NewEquipment(EqGlock)
1313
ak47 := NewEquipment(EqAK47)
1414

15-
pl := NewPlayer()
15+
pl := newPlayer(0)
1616
pl.RawWeapons[1] = &knife
1717
pl.RawWeapons[2] = &glock
1818
pl.RawWeapons[3] = &ak47
1919
pl.ActiveWeaponID = 3
2020

2121
assert.Equal(t, &ak47, pl.ActiveWeapon(), "Should have AK-47 equipped")
2222
}
23+
2324
func TestPlayerWeapons(t *testing.T) {
2425
knife := NewEquipment(EqKnife)
2526
glock := NewEquipment(EqGlock)
2627
ak47 := NewEquipment(EqAK47)
2728

28-
pl := NewPlayer()
29+
pl := newPlayer(0)
2930
pl.RawWeapons[1] = &knife
3031
pl.RawWeapons[2] = &glock
3132
pl.RawWeapons[3] = &ak47
@@ -35,7 +36,7 @@ func TestPlayerWeapons(t *testing.T) {
3536
}
3637

3738
func TestPlayerAlive(t *testing.T) {
38-
pl := NewPlayer()
39+
pl := newPlayer(0)
3940

4041
pl.Hp = 100
4142
assert.Equal(t, true, pl.IsAlive(), "Should be alive")
@@ -51,20 +52,37 @@ func TestPlayerAlive(t *testing.T) {
5152
}
5253

5354
func TestPlayerFlashed(t *testing.T) {
54-
pl := NewPlayer()
55+
pl := newPlayer(128)
5556

5657
assert.False(t, pl.IsBlinded(), "Should not be flashed")
5758

5859
pl.FlashDuration = 2.3
60+
pl.FlashTick = 50
5961
assert.True(t, pl.IsBlinded(), "Should be flashed")
6062
}
6163

64+
func TestPlayerFlashed_FlashDuration_Over(t *testing.T) {
65+
pl := newPlayer(128 * 3)
66+
67+
pl.FlashDuration = 1.9
68+
pl.FlashTick = 128
69+
assert.False(t, pl.IsBlinded(), "Should not be flashed")
70+
}
71+
6272
func TestPlayer_FlashDurationTime(t *testing.T) {
63-
p := NewPlayer()
73+
p := newPlayer(0)
6474

6575
assert.Equal(t, time.Duration(0), p.FlashDurationTime())
6676

6777
p.FlashDuration = 2.3
6878

6979
assert.Equal(t, 2300*time.Millisecond, p.FlashDurationTime())
7080
}
81+
82+
func newPlayer(tick int) *Player {
83+
return NewPlayer(128, tickProvider(tick))
84+
}
85+
86+
func tickProvider(tick int) ingameTickProvider {
87+
return func() int { return tick }
88+
}

datatables.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ func (p *Parser) bindNewPlayer(playerEntity st.IEntity) {
190190
if pl == nil {
191191
isNew = true
192192

193-
pl = common.NewPlayer()
193+
// TODO: read tickRate from CVARs as fallback
194+
pl = common.NewPlayer(p.header.TickRate(), p.gameState.IngameTick)
194195
pl.Name = rp.name
195196
pl.SteamID = rp.xuid
196197
pl.IsBot = rp.isFakePlayer || rp.guid == "BOT"
@@ -231,7 +232,14 @@ func (p *Parser) bindNewPlayer(playerEntity st.IEntity) {
231232

232233
playerEntity.BindProperty("m_angEyeAngles[1]", &pl.ViewDirectionX, st.ValTypeFloat32)
233234
playerEntity.BindProperty("m_angEyeAngles[0]", &pl.ViewDirectionY, st.ValTypeFloat32)
234-
playerEntity.BindProperty("m_flFlashDuration", &pl.FlashDuration, st.ValTypeFloat32)
235+
playerEntity.FindProperty("m_flFlashDuration").OnUpdate(func(val st.PropertyValue) {
236+
if val.FloatVal == 0 {
237+
pl.FlashTick = 0
238+
} else {
239+
pl.FlashTick = p.gameState.ingameTick
240+
}
241+
pl.FlashDuration = val.FloatVal
242+
})
235243

236244
// Velocity
237245
playerEntity.BindProperty("localdata.m_vecVelocity[0]", &pl.Velocity.X, st.ValTypeFloat64)

datatables_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/stretchr/testify/assert"
77
"github.com/stretchr/testify/mock"
88

9+
"github.com/markus-wa/demoinfocs-golang/common"
910
"github.com/markus-wa/demoinfocs-golang/events"
1011
st "github.com/markus-wa/demoinfocs-golang/sendtables"
1112
fakest "github.com/markus-wa/demoinfocs-golang/sendtables/fake"
@@ -19,7 +20,7 @@ func (DevNullReader) Read(p []byte) (n int, err error) {
1920
}
2021

2122
func TestParser_BindNewPlayer_Issue98(t *testing.T) {
22-
p := NewParser(new(DevNullReader))
23+
p := newParser()
2324

2425
p.rawPlayers = map[int]*playerInfo{
2526
0: {
@@ -45,7 +46,7 @@ func TestParser_BindNewPlayer_Issue98(t *testing.T) {
4546
}
4647

4748
func TestParser_BindNewPlayer_Issue98_Reconnect(t *testing.T) {
48-
p := NewParser(new(DevNullReader))
49+
p := newParser()
4950

5051
p.rawPlayers = map[int]*playerInfo{
5152
0: {
@@ -69,6 +70,12 @@ func TestParser_BindNewPlayer_Issue98_Reconnect(t *testing.T) {
6970

7071
}
7172

73+
func newParser() *Parser {
74+
p := NewParser(new(DevNullReader))
75+
p.header = &common.DemoHeader{}
76+
return p
77+
}
78+
7279
func fakePlayerEntity(id int) st.IEntity {
7380
entity := new(fakest.Entity)
7481
entity.On("ID").Return(id)

events/events_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
func TestPlayerFlashed_FlashDuration(t *testing.T) {
13-
p := common.NewPlayer()
13+
p := common.NewPlayer(128, func() int { return 0 })
1414
e := PlayerFlashed{Player: p}
1515

1616
assert.Equal(t, time.Duration(0), e.FlashDuration())

fake/parser_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ func TestParseNextFrameEvents(t *testing.T) {
5252
func kill(wepType common.EquipmentElement) events.Kill {
5353
wep := common.NewEquipment(wepType)
5454
return events.Kill{
55-
Killer: common.NewPlayer(),
55+
Killer: new(common.Player),
5656
Weapon: &wep,
57-
Victim: common.NewPlayer(),
57+
Victim: new(common.Player),
5858
}
5959
}
6060

game_state_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,15 @@ func TestParticipants_SuppressNoEntity(t *testing.T) {
138138
gs := newGameState()
139139
pl := newPlayer()
140140
gs.playersByUserID[0] = pl
141-
gs.playersByUserID[1] = common.NewPlayer()
141+
gs.playersByUserID[1] = common.NewPlayer(0, func() int { return 0 })
142142

143143
allPlayers := gs.Participants().All()
144144

145145
assert.ElementsMatch(t, []*common.Player{pl}, allPlayers)
146146
}
147147

148148
func newPlayer() *common.Player {
149-
pl := common.NewPlayer()
149+
pl := common.NewPlayer(0, func() int { return 0 })
150150
pl.Entity = new(st.Entity)
151151
return pl
152152
}

0 commit comments

Comments
 (0)