Skip to content

Commit 9c2b44a

Browse files
committed
fix: possible nil Weapon field in player hurt events
Regression introduced with this v5 commit 097b925 and noticed while backporting it into the v4, see the CI https://github.com/markus-wa/demoinfocs-golang/actions/runs/16401553730/job/46341730756?pr=599. We still group grenades by type to fix the UniqueID2() issue and store grenades in a slice like it was before.
1 parent 83c839a commit 9c2b44a

File tree

4 files changed

+46
-19
lines changed

4 files changed

+46
-19
lines changed

pkg/demoinfocs/game_events.go

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ func (geh gameEventHandler) clearGrenadeProjectiles() {
324324
}
325325

326326
// Thrown grenades could not be deleted at the end of the round (if they are thrown at the very end, they never get destroyed)
327-
geh.gameState().thrownGrenades = make(map[*common.Player]map[common.EquipmentType]*common.Equipment)
327+
geh.gameState().thrownGrenades = make(map[*common.Player]map[common.EquipmentType][]*common.Equipment)
328328
geh.gameState().flyingFlashbangs = make([]*FlyingFlashbang, 0)
329329
}
330330

@@ -1022,10 +1022,10 @@ func (geh gameEventHandler) addThrownGrenade(p *common.Player, wep *common.Equip
10221022

10231023
gameState := geh.gameState()
10241024
if gameState.thrownGrenades[p] == nil {
1025-
gameState.thrownGrenades[p] = make(map[common.EquipmentType]*common.Equipment)
1025+
gameState.thrownGrenades[p] = make(map[common.EquipmentType][]*common.Equipment)
10261026
}
10271027

1028-
gameState.thrownGrenades[p][wep.Type] = wep
1028+
gameState.thrownGrenades[p][wep.Type] = append(gameState.thrownGrenades[p][wep.Type], wep)
10291029
}
10301030

10311031
func (geh gameEventHandler) getThrownGrenade(p *common.Player, wepType common.EquipmentType) *common.Equipment {
@@ -1034,11 +1034,32 @@ func (geh gameEventHandler) getThrownGrenade(p *common.Player, wepType common.Eq
10341034
return nil
10351035
}
10361036

1037-
if geh.gameState().thrownGrenades[p] == nil {
1037+
playerGrenades := geh.gameState().thrownGrenades[p]
1038+
grenades := playerGrenades[wepType]
1039+
1040+
if len(grenades) == 0 {
1041+
// Molotovs/incendiaries may be reported as the opposite type in game-events. (i.e. incendiary reported as molotov and vice versa)
1042+
switch wepType { //nolint:exhaustive
1043+
case common.EqIncendiary:
1044+
grenades = playerGrenades[common.EqMolotov]
1045+
case common.EqMolotov:
1046+
grenades = playerGrenades[common.EqIncendiary]
1047+
}
1048+
}
1049+
1050+
if len(grenades) == 0 {
1051+
// The player might be controlling a bot, in such case the thrown grenade is stored in the bot's state.
1052+
bot := p.ControlledBot()
1053+
if bot != nil && bot.SteamID64 != p.SteamID64 {
1054+
return geh.getThrownGrenade(bot, wepType)
1055+
}
1056+
}
1057+
1058+
if len(grenades) == 0 {
10381059
return nil
10391060
}
10401061

1041-
return geh.gameState().thrownGrenades[p][wepType]
1062+
return grenades[len(grenades)-1]
10421063
}
10431064

10441065
func (geh gameEventHandler) deleteThrownGrenade(p *common.Player, wepType common.EquipmentType) {
@@ -1047,8 +1068,21 @@ func (geh gameEventHandler) deleteThrownGrenade(p *common.Player, wepType common
10471068
return
10481069
}
10491070

1050-
delete(geh.gameState().thrownGrenades[p], wepType)
1071+
playerGrenades := geh.gameState().thrownGrenades[p]
1072+
if len(playerGrenades) == 0 {
1073+
return
1074+
}
1075+
1076+
grenades := playerGrenades[wepType]
1077+
if len(grenades) == 0 {
1078+
return
1079+
}
10511080

1081+
// Delete the first grenade thrown by the player and this grenade type.
1082+
playerGrenades[wepType] = grenades[:len(grenades)-1]
1083+
if len(playerGrenades[wepType]) == 0 {
1084+
delete(playerGrenades, wepType)
1085+
}
10521086
}
10531087

10541088
func (geh gameEventHandler) attackerWeaponType(wepType common.EquipmentType, victimUserID int32) common.EquipmentType {
@@ -1082,13 +1116,6 @@ func (geh gameEventHandler) getEquipmentInstance(player *common.Player, wepType
10821116
return getPlayerWeapon(player, wepType)
10831117
}
10841118

1085-
// checks if two EquipmentElements are the same, considering that incendiary and molotov should be treated as identical
1086-
func isSameEquipmentElement(a common.EquipmentType, b common.EquipmentType) bool {
1087-
return a == b ||
1088-
(a == common.EqIncendiary && b == common.EqMolotov) ||
1089-
(b == common.EqIncendiary && a == common.EqMolotov)
1090-
}
1091-
10921119
// Returns the players instance of the weapon if applicable or a new instance otherwise.
10931120
func getPlayerWeapon(player *common.Player, wepType common.EquipmentType) *common.Equipment {
10941121
if player != nil {

pkg/demoinfocs/game_events_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func TestAddThrownGrenade(t *testing.T) {
115115

116116
assert.NotEmpty(t, p.gameState.thrownGrenades)
117117
assert.NotEmpty(t, p.gameState.thrownGrenades[pl])
118-
assert.Equal(t, p.gameState.thrownGrenades[pl][common.EqHE], he)
118+
assert.Equal(t, p.gameState.thrownGrenades[pl][common.EqHE][0], he)
119119
}
120120

121121
func TestGetThrownGrenade_NilPlayer(t *testing.T) {

pkg/demoinfocs/game_state.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ type gameState struct {
3737
isFreezetime bool
3838
isMatchStarted bool
3939
overtimeCount int
40-
lastFlash lastFlash // Information about the last flash that exploded, used to find the attacker and projectile for player_blind events
41-
currentDefuser *common.Player // Player currently defusing the bomb, if any
42-
currentPlanter *common.Player // Player currently planting the bomb, if any
43-
thrownGrenades map[*common.Player]map[common.EquipmentType]*common.Equipment // Information about every player's thrown grenades (from the moment they are thrown to the moment their effect is ended)
40+
lastFlash lastFlash // Information about the last flash that exploded, used to find the attacker and projectile for player_blind events
41+
currentDefuser *common.Player // Player currently defusing the bomb, if any
42+
currentPlanter *common.Player // Player currently planting the bomb, if any
43+
thrownGrenades map[*common.Player]map[common.EquipmentType][]*common.Equipment // Information about every player's thrown grenades (from the moment they are thrown to the moment their effect is ended)
4444
rules gameRules
4545
demoInfo demoInfoProvider
4646
lastRoundStartEvent *events.RoundStart // Used to dispatch this event after a possible MatchStartedChanged event
@@ -249,7 +249,7 @@ func newGameState(demoInfo demoInfoProvider) *gameState {
249249
weapons: make(map[int]*common.Equipment),
250250
hostages: make(map[int]*common.Hostage),
251251
entities: make(map[int]st.Entity),
252-
thrownGrenades: make(map[*common.Player]map[common.EquipmentType]*common.Equipment),
252+
thrownGrenades: make(map[*common.Player]map[common.EquipmentType][]*common.Equipment),
253253
flyingFlashbangs: make([]*FlyingFlashbang, 0),
254254
lastFlash: lastFlash{
255255
projectileByPlayer: make(map[*common.Player]*common.GrenadeProjectile),

test/default.golden

-95 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)