From 2de26bea472b20aa6a2c5b150f5e8c12b56cf85a Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Wed, 22 Jul 2026 23:45:37 +0100 Subject: [PATCH 01/10] Fix damage+hit data sync issue + console data sync issue First is to drop unnecessary prediction for damages variables and second is to make sure the console takes the damage+hit values straight from the server instead which matches up with scoreboard's synced damage data. TODO: Maybe the scoreboard can also just use the console and drop the syncing entirely? --- src/game/client/neo/c_neo_player.cpp | 4 -- .../client/neo/neo_killer_damage_info.cpp | 63 +++++++++---------- src/game/server/neo/neo_player.cpp | 53 +++++++++++++++- 3 files changed, 81 insertions(+), 39 deletions(-) diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index 9c34034ff..53e74be4d 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -117,10 +117,6 @@ IMPLEMENT_CLIENTCLASS_DT(C_NEO_Player, DT_NEO_Player, CNEO_Player) END_RECV_TABLE() BEGIN_PREDICTION_DATA(C_NEO_Player) - DEFINE_PRED_ARRAY(m_rfAttackersScores, FIELD_INTEGER, MAX_PLAYERS_ARRAY_SAFE, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_ARRAY(m_rfAttackersAccumlator, FIELD_FLOAT, MAX_PLAYERS_ARRAY_SAFE, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_ARRAY(m_rfAttackersHits, FIELD_INTEGER, MAX_PLAYERS_ARRAY_SAFE, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_FIELD_TOL(m_flCamoAuxLastTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE), DEFINE_PRED_FIELD(m_bInThermOpticCamo, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE), diff --git a/src/game/client/neo/neo_killer_damage_info.cpp b/src/game/client/neo/neo_killer_damage_info.cpp index 52f88ad6e..0e1d811ee 100644 --- a/src/game/client/neo/neo_killer_damage_info.cpp +++ b/src/game/client/neo/neo_killer_damage_info.cpp @@ -23,8 +23,6 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) // Print damage stats into the console // Print to console - AttackersTotals totals = {}; - const int thisIdx = localPlayer->entindex(); // Can't rely on Msg as it can print out of order, so do it in chunks @@ -94,15 +92,20 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) } ConMsg("%sDamage infos (Round %d):\n%s\n", BORDER, NEORules()->roundNumber(), setKillByLine ? killByLine : ""); - - for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) + + AttackersTotals totals = {}; + + // Read per-player damage stats from the message (authoritative server data) + const int iAtkSize = static_cast(msg.ReadShort()); + for (int i = 0; i < iAtkSize; ++i) { - if (pIdx == thisIdx) - { - continue; - } + const short pIdx = msg.ReadShort(); + const short dealtDmgs = msg.ReadShort(); + const short dealtHits = msg.ReadShort(); + const short takenDmgs = msg.ReadShort(); + const short takenHits = msg.ReadShort(); - auto* neoAttacker = assert_cast(UTIL_PlayerByIndex(pIdx)); + auto *neoAttacker = assert_cast(UTIL_PlayerByIndex(pIdx)); if (!neoAttacker || neoAttacker->IsHLTV()) { continue; @@ -114,32 +117,26 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) continue; } - const AttackersTotals attackerInfo = { - .dealtDmgs = neoAttacker->GetAttackersScores(thisIdx), - .dealtHits = neoAttacker->GetAttackerHits(thisIdx), - .takenDmgs = localPlayer->GetAttackersScores(pIdx), - .takenHits = localPlayer->GetAttackerHits(pIdx), - }; - if (attackerInfo.dealtDmgs > 0 || attackerInfo.takenDmgs > 0) - { - const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); + const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); - char infoLine[128] = {}; - if (attackerInfo.dealtDmgs > 0) - { - V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", - dmgerName, dmgerClass, - attackerInfo.dealtDmgs, attackerInfo.dealtHits); - } - if (attackerInfo.takenDmgs > 0) - { - V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", - dmgerName, dmgerClass, - attackerInfo.takenDmgs, attackerInfo.takenHits); - } + char infoLine[128] = {}; + if (dealtDmgs > 0) + { + V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + dealtDmgs, dealtHits); ConMsg("%s", infoLine); - - totals += attackerInfo; + totals.takenDmgs += dealtDmgs; + totals.takenHits += dealtHits; + } + if (takenDmgs > 0) + { + V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + takenDmgs, takenHits); + ConMsg("%s", infoLine); + totals.dealtDmgs += takenDmgs; + totals.dealtHits += takenHits; } } diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 268ac07c4..6d9c30a19 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -76,9 +76,9 @@ SendPropTime(SENDINFO(m_flNextPingTime)), SendPropString(SENDINFO(m_pszTestMessage)), -SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersScores)), m_rfAttackersScores), +SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersScores), -1, SPROP_CHANGES_OFTEN), m_rfAttackersScores), SendPropArray(SendPropFloat(SENDINFO_ARRAY(m_rfAttackersAccumlator), -1, SPROP_COORD_MP_LOWPRECISION | SPROP_CHANGES_OFTEN, MIN_COORD_FLOAT, MAX_COORD_FLOAT), m_rfAttackersAccumlator), -SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersHits)), m_rfAttackersHits), +SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersHits), -1, SPROP_CHANGES_OFTEN), m_rfAttackersHits), SendPropArray(SendPropVector(SENDINFO_ARRAY(m_vLastPingByStar), -1, SPROP_COORD), m_vLastPingByStar), SendPropInt(SENDINFO(m_NeoFlags), 4, SPROP_UNSIGNED), @@ -2347,6 +2347,55 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) } WRITE_SHORT(attackerIdx); WRITE_STRING(killedWithName); + + AttackersTotals atkTotals[MAX_PLAYERS_ARRAY_SAFE] = {}; + int atkPlayerIdxs[MAX_PLAYERS_ARRAY_SAFE] = {}; + int iAtkSize = 0; + + // Send authoritative per-player damage stats directly from server + const int thisIdx = entindex(); + for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) + { + if (pIdx == thisIdx) + { + continue; + } + + auto *pNeoOther = static_cast(UTIL_PlayerByIndex(pIdx)); + if (!pNeoOther || pNeoOther->IsHLTV()) + { + continue; + } + + const int dealtDmgs = pNeoOther->GetAttackersScores(thisIdx); + const int dealtHits = pNeoOther->GetAttackerHits(thisIdx); + const int takenDmgs = GetAttackersScores(pIdx); + const int takenHits = GetAttackerHits(pIdx); + + if ((dealtDmgs > 0 && dealtHits > 0) || (takenDmgs > 0 && takenHits > 0)) + { + atkPlayerIdxs[iAtkSize] = pIdx; + + AttackersTotals *atk = &atkTotals[iAtkSize]; + atk->dealtDmgs = dealtDmgs; + atk->dealtHits = dealtHits; + atk->takenDmgs = takenDmgs; + atk->takenHits = takenHits; + + ++iAtkSize; + } + } + + WRITE_SHORT(iAtkSize); + for (int i = 0; i < iAtkSize; ++i) + { + WRITE_SHORT(atkPlayerIdxs[i]); + const AttackersTotals *atk = &atkTotals[i]; + WRITE_SHORT(static_cast(atk->dealtDmgs)); + WRITE_SHORT(static_cast(atk->dealtHits)); + WRITE_SHORT(static_cast(atk->takenDmgs)); + WRITE_SHORT(static_cast(atk->takenHits)); + } } MessageEnd(); } From fc094a0f3550067596773ade8bf979fda0de954b Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Thu, 23 Jul 2026 21:27:34 +0100 Subject: [PATCH 02/10] Remove m_rfAttacker arrays from the client, just rely on UserMessage The previous commit has demoed when the server sends over the damage hit values via UserMessage, this is the reliable and actual number the server have. So if we're using UserMessage, might as well have the scoreboard take it from that instead and save having to transmit those arrays. Also enabled damage report for deathmatch, properly clearing index other held on spawning. * fixes #1874 --- game/neo/scripts/HudLayout.res | 9 +- src/game/client/CMakeLists.txt | 5 +- ...info.cpp => c_neo_killer_damage_infos.cpp} | 52 ++++++--- ...er_infos.h => c_neo_killer_damage_infos.h} | 5 + src/game/client/neo/c_neo_killer_infos.cpp | 10 -- src/game/client/neo/c_neo_player.cpp | 45 +++----- src/game/client/neo/c_neo_player.h | 9 +- .../client/neo/ui/neo_hud_deathnotice.cpp | 2 +- .../client/neo/ui/neo_hud_killer_info.cpp | 4 +- src/game/client/neo/ui/neo_scoreboard.cpp | 77 +++++++------ src/game/server/neo/neo_player.cpp | 106 ++++++------------ src/game/server/neo/neo_player.h | 10 +- src/game/shared/neo/neo_gamerules.cpp | 16 +-- src/game/shared/neo/neo_player_shared.h | 1 + 14 files changed, 149 insertions(+), 202 deletions(-) rename src/game/client/neo/{neo_killer_damage_info.cpp => c_neo_killer_damage_infos.cpp} (76%) rename src/game/client/neo/{c_neo_killer_infos.h => c_neo_killer_damage_infos.h} (77%) delete mode 100644 src/game/client/neo/c_neo_killer_infos.cpp diff --git a/game/neo/scripts/HudLayout.res b/game/neo/scripts/HudLayout.res index eff196c6d..d692ff1c0 100644 --- a/game/neo/scripts/HudLayout.res +++ b/game/neo/scripts/HudLayout.res @@ -998,14 +998,9 @@ "tall" "200" } - neo_killer_damage_info + neo_killer_info { - "fieldName" "neo_killer_damage_info" - "xpos" "20" - "ypos" "150" - "wide" "640" - "tall" "480" - "box_color" "150 150 150 60" + "fieldName" "neo_killer_info" } neo_context_hint diff --git a/src/game/client/CMakeLists.txt b/src/game/client/CMakeLists.txt index b5c6c9002..9273ef749 100644 --- a/src/game/client/CMakeLists.txt +++ b/src/game/client/CMakeLists.txt @@ -1574,8 +1574,7 @@ set(UNITY_SOURCE_NEO_SRC_CLIENT neo/c_neo_te_tocflash.cpp neo/neo_fixup_glshaders.cpp neo/c_neo_bloom_controller.cpp - neo/neo_killer_damage_info.cpp - neo/c_neo_killer_infos.cpp + neo/c_neo_killer_damage_infos.cpp ) set_source_files_properties( @@ -1592,7 +1591,7 @@ target_sources_grouped( neo/c_neo_player.h neo/c_neo_te_tocflash.h neo/neo_fixup_glshaders.h - neo/c_neo_killer_infos.h + neo/c_neo_killer_damage_infos.h ${UNITY_SOURCE_NEO_SRC_CLIENT} ) diff --git a/src/game/client/neo/neo_killer_damage_info.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp similarity index 76% rename from src/game/client/neo/neo_killer_damage_info.cpp rename to src/game/client/neo/c_neo_killer_damage_infos.cpp index 0e1d811ee..27a0317f7 100644 --- a/src/game/client/neo/neo_killer_damage_info.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -1,4 +1,7 @@ +#include "c_neo_killer_damage_infos.h" + #include +#include "strtools.h" #include "c_neo_player.h" #include "neo_gamerules.h" @@ -8,6 +11,18 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +void NeoUserIDsLocalKilledClear() +{ + g_neoUserIDsLocalKilledSize = 0; + V_memset(g_neoUserIDsLocalKilled, 0, sizeof(g_neoUserIDsLocalKilled)); +} + +void NeoDamageReportClear() +{ + g_neoDamageReportSize = 0; + V_memset(g_neoDamageReport, 0, sizeof(g_neoDamageReport)); +} + // Console + activation of damage info static void __MsgFunc_KillerDamageInfo(bf_read &msg) { @@ -93,19 +108,21 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) ConMsg("%sDamage infos (Round %d):\n%s\n", BORDER, NEORules()->roundNumber(), setKillByLine ? killByLine : ""); + NeoDamageReportClear(); AttackersTotals totals = {}; // Read per-player damage stats from the message (authoritative server data) - const int iAtkSize = static_cast(msg.ReadShort()); + const int iAtkSize = msg.ReadShort(); for (int i = 0; i < iAtkSize; ++i) { - const short pIdx = msg.ReadShort(); - const short dealtDmgs = msg.ReadShort(); - const short dealtHits = msg.ReadShort(); - const short takenDmgs = msg.ReadShort(); - const short takenHits = msg.ReadShort(); - - auto *neoAttacker = assert_cast(UTIL_PlayerByIndex(pIdx)); + AttackersTotals *pDmgReport = &g_neoDamageReport[g_neoDamageReportSize]; + pDmgReport->iUserID = msg.ReadShort(); + pDmgReport->dealtDmgs = msg.ReadShort(); + pDmgReport->dealtHits = msg.ReadShort(); + pDmgReport->takenDmgs = msg.ReadShort(); + pDmgReport->takenHits = msg.ReadShort(); + + auto *neoAttacker = USERID2NEOPLAYER(pDmgReport->iUserID); if (!neoAttacker || neoAttacker->IsHLTV()) { continue; @@ -120,24 +137,26 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); char infoLine[128] = {}; - if (dealtDmgs > 0) + if (pDmgReport->dealtDmgs > 0 && pDmgReport->dealtHits > 0) { V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", dmgerName, dmgerClass, - dealtDmgs, dealtHits); + pDmgReport->dealtDmgs, pDmgReport->dealtHits); ConMsg("%s", infoLine); - totals.takenDmgs += dealtDmgs; - totals.takenHits += dealtHits; + totals.takenDmgs += pDmgReport->dealtDmgs; + totals.takenHits += pDmgReport->dealtHits; } - if (takenDmgs > 0) + if (pDmgReport->takenDmgs > 0 && pDmgReport->takenHits > 0) { V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", dmgerName, dmgerClass, - takenDmgs, takenHits); + pDmgReport->takenDmgs, pDmgReport->takenHits); ConMsg("%s", infoLine); - totals.dealtDmgs += takenDmgs; - totals.dealtHits += takenHits; + totals.dealtDmgs += pDmgReport->takenDmgs; + totals.dealtHits += pDmgReport->takenHits; } + + ++g_neoDamageReportSize; } ConMsg("Total damage dealt: %d in %d hits\nTotal damage received from players: %d in %d hits\n%s\n", @@ -146,3 +165,4 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) BORDER); } USER_MESSAGE_REGISTER(KillerDamageInfo); + diff --git a/src/game/client/neo/c_neo_killer_infos.h b/src/game/client/neo/c_neo_killer_damage_infos.h similarity index 77% rename from src/game/client/neo/c_neo_killer_infos.h rename to src/game/client/neo/c_neo_killer_damage_infos.h index a017b2a49..336286fc7 100644 --- a/src/game/client/neo/c_neo_killer_infos.h +++ b/src/game/client/neo/c_neo_killer_damage_infos.h @@ -1,6 +1,7 @@ #pragma once #include "shareddefs.h" +#include "neo_player_shared.h" static constexpr const int WEP_NAME_MAXSTRLEN = 32; @@ -16,9 +17,13 @@ struct CNEOKillerInfos }; void NeoUserIDsLocalKilledClear(); +void NeoDamageReportClear(); // Global instance of CNEOKillerInfos and local-player's kill record inline CNEOKillerInfos g_neoKillerInfos; inline int g_neoUserIDsLocalKilledSize; inline int g_neoUserIDsLocalKilled[MAX_PLAYERS_ARRAY_SAFE]; +inline int g_neoDamageReportSize; +inline AttackersTotals g_neoDamageReport[MAX_PLAYERS_ARRAY_SAFE]; + diff --git a/src/game/client/neo/c_neo_killer_infos.cpp b/src/game/client/neo/c_neo_killer_infos.cpp deleted file mode 100644 index 08ef6e39a..000000000 --- a/src/game/client/neo/c_neo_killer_infos.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "c_neo_killer_infos.h" - -#include "strtools.h" - -void NeoUserIDsLocalKilledClear() -{ - g_neoUserIDsLocalKilledSize = 0; - V_memset(g_neoUserIDsLocalKilled, 0, sizeof(g_neoUserIDsLocalKilled)); -} - diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index 53e74be4d..973e710dd 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -48,7 +48,7 @@ #include "rendertexture.h" #include "ivieweffects.h" #include "iachievementmgr.h" -#include "c_neo_killer_infos.h" +#include "c_neo_killer_damage_infos.h" #include #include @@ -98,9 +98,6 @@ IMPLEMENT_CLIENTCLASS_DT(C_NEO_Player, DT_NEO_Player, CNEO_Player) RecvPropTime(RECVINFO(m_flJumpLastTime)), RecvPropTime(RECVINFO(m_flNextPingTime)), - RecvPropArray(RecvPropInt(RECVINFO(m_rfAttackersScores[0])), m_rfAttackersScores), - RecvPropArray(RecvPropFloat(RECVINFO(m_rfAttackersAccumlator[0])), m_rfAttackersAccumlator), - RecvPropArray(RecvPropInt(RECVINFO(m_rfAttackersHits[0])), m_rfAttackersHits), RecvPropArray(RecvPropVector(RECVINFO(m_vLastPingByStar[0])), m_vLastPingByStar), RecvPropInt(RECVINFO(m_NeoFlags)), @@ -475,7 +472,7 @@ C_NEO_Player::C_NEO_Player() m_flTocFactor = 0.15f; memset(m_szNeoNameWDupeIdx, 0, sizeof(m_szNeoNameWDupeIdx)); - if (IsLocalPlayer()) NeoUserIDsLocalKilledClear(); + ClearLocalPlayerDmgReports(); m_szNameDupePos = 0; } @@ -569,15 +566,6 @@ void C_NEO_Player::CheckLeanButtons() } } -int C_NEO_Player::GetAttackersScores(const int attackerIdx) const -{ - if (NEORules()->GetGameType() == NEO_GAME_TYPE_DM || NEORules()->GetGameType() == NEO_GAME_TYPE_TDM) - { - return m_rfAttackersScores.Get(attackerIdx); - } - return m_rfAttackersScores.Get(attackerIdx); -} - const char *C_NEO_Player::GetNeoClantag() const { if (!sv_neo_clantag_allow.GetBool() || @@ -646,11 +634,6 @@ bool C_NEO_Player::ClientWantNeoName() const return m_bClientWantNeoName; } -int C_NEO_Player::GetAttackerHits(const int attackerIdx) const -{ - return m_rfAttackersHits.Get(attackerIdx); -} - ConVar cl_neo_hud_health_mode("cl_neo_hud_health_mode", "1", FCVAR_ARCHIVE, "Health display mode. 0 = Percent, 1 = Hit points, 2 = Effective hit points", true, 0, true, 2); @@ -1204,8 +1187,7 @@ void C_NEO_Player::PreThink( void ) CLocalPlayerFilter filter; enginesound->SetPlayerDSP(filter, 0, true); - NeoUserIDsLocalKilledClear(); - V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + ClearLocalPlayerDmgReports(); // Reset the cache of other players crosshair data on spawning in if (CHudCrosshair *crosshair = GET_HUDELEMENT(CHudCrosshair)) @@ -1616,16 +1598,7 @@ void C_NEO_Player::Spawn( void ) m_nVisionLastTick = 0; m_bInLean = NEO_LEAN_NONE; - static_assert(_ARRAYSIZE(m_rfAttackersScores) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersAccumlator) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersHits) == MAX_PLAYERS_ARRAY_SAFE); - for (int i = 0; i < MAX_PLAYERS_ARRAY_SAFE; ++i) - { - m_rfAttackersScores.GetForModify(i) = 0; - m_rfAttackersAccumlator.GetForModify(i) = 0.0f; - m_rfAttackersHits.GetForModify(i) = 0; - } - if (IsLocalPlayer()) NeoUserIDsLocalKilledClear(); + ClearLocalPlayerDmgReports(); Weapon_SetZoom(false); @@ -2163,3 +2136,13 @@ void C_NEO_Player::PlayerUse() } } } + +void C_NEO_Player::ClearLocalPlayerDmgReports() +{ + if (IsLocalPlayer()) + { + NeoDamageReportClear(); + NeoUserIDsLocalKilledClear(); + V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + } +} diff --git a/src/game/client/neo/c_neo_player.h b/src/game/client/neo/c_neo_player.h index a2bef4f7c..607d7185e 100644 --- a/src/game/client/neo/c_neo_player.h +++ b/src/game/client/neo/c_neo_player.h @@ -172,9 +172,6 @@ class C_NEO_Player : public C_HL2MP_Player bool IsInVision() const { return m_bInVision; } bool IsInAim() const { return m_bInAim; } - int GetAttackersScores(const int attackerIdx) const; - int GetAttackerHits(const int attackerIdx) const; - const char *InternalGetNeoPlayerName() const; const char *GetNeoPlayerName() const; bool ClientWantNeoName() const; @@ -212,6 +209,8 @@ class C_NEO_Player : public C_HL2MP_Player bool IsAllowedToSuperJump(void); + void ClearLocalPlayerDmgReports(); + // Spectator takeover player related functionality bool IsAFK() const; bool IsFakePlayer() const; @@ -224,10 +223,6 @@ class C_NEO_Player : public C_HL2MP_Player CNetworkVar(int, m_iXP); CNetworkVar(int, m_iLoadoutWepChoice); CNetworkVar(int, m_iNextSpawnClassChoice); - - CNetworkArray(int, m_rfAttackersScores, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(float, m_rfAttackersAccumlator, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(int, m_rfAttackersHits, MAX_PLAYERS_ARRAY_SAFE); CNetworkVar(bool, m_bHasBeenAirborneForTooLongToSuperJump); diff --git a/src/game/client/neo/ui/neo_hud_deathnotice.cpp b/src/game/client/neo/ui/neo_hud_deathnotice.cpp index 42099f06a..463bcb68c 100644 --- a/src/game/client/neo/ui/neo_hud_deathnotice.cpp +++ b/src/game/client/neo/ui/neo_hud_deathnotice.cpp @@ -22,7 +22,7 @@ #include "neo_hud_childelement.h" #include "spectatorgui.h" #include "takedamageinfo.h" -#include "c_neo_killer_infos.h" +#include "c_neo_killer_damage_infos.h" #include "neo_scoreboard.h" // memdbgon must be the last include file in a .cpp file!!! diff --git a/src/game/client/neo/ui/neo_hud_killer_info.cpp b/src/game/client/neo/ui/neo_hud_killer_info.cpp index 86b558613..9bf7d99f4 100644 --- a/src/game/client/neo/ui/neo_hud_killer_info.cpp +++ b/src/game/client/neo/ui/neo_hud_killer_info.cpp @@ -3,7 +3,7 @@ #include "iclientmode.h" -#include "c_neo_killer_infos.h" +#include "c_neo_killer_damage_infos.h" #include "c_neo_player.h" #include "neo_gamerules.h" #include "vgui/ILocalize.h" @@ -17,7 +17,7 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" -DECLARE_NAMED_HUDELEMENT(CNEOHud_KillerInfo, neo_killer_damage_info) +DECLARE_NAMED_HUDELEMENT(CNEOHud_KillerInfo, neo_killer_info) NEO_HUD_ELEMENT_DECLARE_FREQ_CVAR(KillerInfo, 0.25) diff --git a/src/game/client/neo/ui/neo_scoreboard.cpp b/src/game/client/neo/ui/neo_scoreboard.cpp index 777741ef5..85b0442ff 100644 --- a/src/game/client/neo/ui/neo_scoreboard.cpp +++ b/src/game/client/neo/ui/neo_scoreboard.cpp @@ -3,7 +3,7 @@ #include "c_neo_player.h" #include "neo_gamerules.h" #include "neo_theme.h" -#include "c_neo_killer_infos.h" +#include "c_neo_killer_damage_infos.h" #include #include @@ -331,7 +331,14 @@ void CNEOScoreBoard::Update() const bool bLocalPlaying = (TEAM_JINRAI == iLocalPlayerTeam || TEAM_NSF == iLocalPlayerTeam); const bool bIsTeamplay = NEORules()->IsTeamplay(); + const bool bShowDamageInfo = pLocalPlayer->IsPlayerDead() + && NEORules()->InRoundState() + && bLocalPlaying; + + // Zero array every update m_iTotalPlayers = 0; + V_memset(m_playersInfo, 0, sizeof(m_playersInfo)); + for (int i = 1; i <= gpGlobals->maxClients; ++i) { if (false == g_PR->IsConnected(i)) @@ -373,45 +380,46 @@ void CNEOScoreBoard::Update() pPlayerInfo->steamID.Clear(); } - const bool bIsPlaying = (TEAM_JINRAI == pPlayerInfo->iTeam || TEAM_NSF == pPlayerInfo->iTeam); - pPlayerInfo->iXP = bIsPlaying ? g_PR->GetXP(i) : 0; - pPlayerInfo->iDeaths = bIsPlaying ? g_PR->GetDeaths(i) : 0; + const bool bIsPlaying = ( + TEAM_JINRAI == pPlayerInfo->iTeam + || TEAM_NSF == pPlayerInfo->iTeam); - // Damage infos - const bool bShowDamageInfo = pLocalPlayer->IsPlayerDead() - && NEORules()->InRoundState() - && (pLocalPlayer->GetTeamNumber() == TEAM_JINRAI - || pLocalPlayer->GetTeamNumber() == TEAM_NSF) - && bIsTeamplay; - - if (bShowDamageInfo && pNeoPlayer && false == pNeoPlayer->IsHLTV() - && (pNeoPlayer->GetTeamNumber() == TEAM_JINRAI - || pNeoPlayer->GetTeamNumber() == TEAM_NSF)) + if (bIsPlaying) { - pPlayerInfo->iDealtDmgs = pNeoPlayer->GetAttackersScores(pLocalPlayer->entindex()); - pPlayerInfo->iDealtHits = pNeoPlayer->GetAttackerHits(pLocalPlayer->entindex()); - pPlayerInfo->iTakenDmgs = pLocalPlayer->GetAttackersScores(pNeoPlayer->entindex()); - pPlayerInfo->iTakenHits = pLocalPlayer->GetAttackerHits(pNeoPlayer->entindex()); - pPlayerInfo->bKilledYou = (pNeoPlayer->entindex() == g_neoKillerInfos.iEntIndex); - pPlayerInfo->bYouKilled = false; - for (int j = 0; j < g_neoUserIDsLocalKilledSize; ++j) + pPlayerInfo->iXP = g_PR->GetXP(i); + pPlayerInfo->iDeaths = g_PR->GetDeaths(i); + + // Damage infos + if (bShowDamageInfo) { - if (g_neoUserIDsLocalKilled[j] == pPlayerInfo->iUserID) + AttackersTotals *pDmgReport = nullptr; + for (int j = 0; j < g_neoDamageReportSize; ++j) { - pPlayerInfo->bYouKilled = true; - break; + if (g_neoDamageReport[j].iUserID == pPlayerInfo->iUserID) + { + pDmgReport = &g_neoDamageReport[j]; + break; + } + } + if (pDmgReport) + { + pPlayerInfo->iDealtDmgs = pDmgReport->dealtDmgs; + pPlayerInfo->iDealtHits = pDmgReport->dealtHits; + pPlayerInfo->iTakenDmgs = pDmgReport->takenDmgs; + pPlayerInfo->iTakenHits = pDmgReport->takenHits; + pPlayerInfo->bKilledYou = pNeoPlayer ? (pNeoPlayer->entindex() == g_neoKillerInfos.iEntIndex) : false; + pPlayerInfo->bYouKilled = false; + for (int j = 0; j < g_neoUserIDsLocalKilledSize; ++j) + { + if (g_neoUserIDsLocalKilled[j] == pPlayerInfo->iUserID) + { + pPlayerInfo->bYouKilled = true; + break; + } + } } } } - else - { - pPlayerInfo->iDealtDmgs = 0; - pPlayerInfo->iDealtHits = 0; - pPlayerInfo->iTakenDmgs = 0; - pPlayerInfo->iTakenHits = 0; - pPlayerInfo->bKilledYou = false; - pPlayerInfo->bYouKilled = false; - } // pPlayerInfo->iClass const bool bShowClass = @@ -459,7 +467,7 @@ void CNEOScoreBoard::Update() // pPlayerInfo->bDead pPlayerInfo->bDead = (false == g_PR->IsAlive(i)); - if (pPlayerInfo->iTeam == TEAM_JINRAI || pPlayerInfo->iTeam == TEAM_NSF) + if (bIsPlaying) { if (bIsImpersonating) { @@ -577,7 +585,6 @@ void CNEOScoreBoard::OnMainLoop(const NeoUI::Mode eMode) const bool bShowDamageInfo = pLocalPlayer->IsPlayerDead() && NEORules()->InRoundState() && g_neoKillerInfos.bHasDmgInfos - && bIsTeamplay && (pLocalPlayer->GetTeamNumber() == TEAM_JINRAI || pLocalPlayer->GetTeamNumber() == TEAM_NSF); static CrosshairInfo staticXhairInfo = {}; diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 6d9c30a19..e3f4f8c99 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -75,10 +75,6 @@ SendPropTime(SENDINFO(m_flJumpLastTime)), SendPropTime(SENDINFO(m_flNextPingTime)), SendPropString(SENDINFO(m_pszTestMessage)), - -SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersScores), -1, SPROP_CHANGES_OFTEN), m_rfAttackersScores), -SendPropArray(SendPropFloat(SENDINFO_ARRAY(m_rfAttackersAccumlator), -1, SPROP_COORD_MP_LOWPRECISION | SPROP_CHANGES_OFTEN, MIN_COORD_FLOAT, MAX_COORD_FLOAT), m_rfAttackersAccumlator), -SendPropArray(SendPropInt(SENDINFO_ARRAY(m_rfAttackersHits), -1, SPROP_CHANGES_OFTEN), m_rfAttackersHits), SendPropArray(SendPropVector(SENDINFO_ARRAY(m_vLastPingByStar), -1, SPROP_COORD), m_vLastPingByStar), SendPropInt(SENDINFO(m_NeoFlags), 4, SPROP_UNSIGNED), @@ -119,10 +115,6 @@ DEFINE_FIELD(m_flNextPingTime, FIELD_TIME), DEFINE_FIELD(m_pszTestMessage, FIELD_STRING), -DEFINE_FIELD(m_rfAttackersScores, FIELD_CUSTOM), -DEFINE_FIELD(m_rfAttackersAccumlator, FIELD_CUSTOM), -DEFINE_FIELD(m_rfAttackersHits, FIELD_CUSTOM), - DEFINE_FIELD(m_NeoFlags, FIELD_CHARACTER), DEFINE_FIELD(m_szNeoName, FIELD_STRING), @@ -749,14 +741,32 @@ void CNEO_Player::Spawn(void) m_bAllowGibbing = true; m_bIneligibleForLoadoutPick = false; - static_assert(_ARRAYSIZE(m_rfAttackersScores) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersAccumlator) == MAX_PLAYERS_ARRAY_SAFE); - static_assert(_ARRAYSIZE(m_rfAttackersHits) == MAX_PLAYERS_ARRAY_SAFE); - for (int i = 0; i < MAX_PLAYERS_ARRAY_SAFE; ++i) + static_assert(_ARRAYSIZE(m_riAttackersScores) == MAX_PLAYERS_ARRAY_SAFE); + static_assert(_ARRAYSIZE(m_rflAttackersAccumlator) == MAX_PLAYERS_ARRAY_SAFE); + static_assert(_ARRAYSIZE(m_riAttackersHits) == MAX_PLAYERS_ARRAY_SAFE); + V_memset(m_riAttackersScores, 0, sizeof(m_riAttackersScores)); + V_memset(m_rflAttackersAccumlator, 0, sizeof(m_rflAttackersAccumlator)); + V_memset(m_riAttackersHits, 0, sizeof(m_riAttackersHits)); + + // Also set zero on other player's held stats of this player's index, needed + // for gamemodes where player respawn within a round + const int thisIdx = entindex(); + for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) { - m_rfAttackersScores.GetForModify(i) = 0; - m_rfAttackersAccumlator.GetForModify(i) = 0.0f; - m_rfAttackersHits.GetForModify(i) = 0; + if (pIdx == thisIdx) + { + continue; + } + + auto *pNeoOther = static_cast(UTIL_PlayerByIndex(pIdx)); + if (!pNeoOther || pNeoOther->IsHLTV()) + { + continue; + } + + pNeoOther->m_riAttackersScores[thisIdx] = 0; + pNeoOther->m_rflAttackersAccumlator[thisIdx] = 0.0f; + pNeoOther->m_riAttackersHits[thisIdx] = 0; } m_flRanOutSprintTime = 0.0f; @@ -2349,7 +2359,6 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) WRITE_STRING(killedWithName); AttackersTotals atkTotals[MAX_PLAYERS_ARRAY_SAFE] = {}; - int atkPlayerIdxs[MAX_PLAYERS_ARRAY_SAFE] = {}; int iAtkSize = 0; // Send authoritative per-player damage stats directly from server @@ -2367,30 +2376,27 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) continue; } - const int dealtDmgs = pNeoOther->GetAttackersScores(thisIdx); - const int dealtHits = pNeoOther->GetAttackerHits(thisIdx); - const int takenDmgs = GetAttackersScores(pIdx); - const int takenHits = GetAttackerHits(pIdx); + const int dealtDmgs = pNeoOther->m_riAttackersScores[thisIdx]; + const int dealtHits = pNeoOther->m_riAttackersHits[thisIdx]; + const int takenDmgs = m_riAttackersScores[pIdx]; + const int takenHits = m_riAttackersHits[pIdx]; if ((dealtDmgs > 0 && dealtHits > 0) || (takenDmgs > 0 && takenHits > 0)) { - atkPlayerIdxs[iAtkSize] = pIdx; - - AttackersTotals *atk = &atkTotals[iAtkSize]; + AttackersTotals *atk = &atkTotals[iAtkSize++]; + atk->iUserID = pNeoOther->GetUserID(); atk->dealtDmgs = dealtDmgs; atk->dealtHits = dealtHits; atk->takenDmgs = takenDmgs; atk->takenHits = takenHits; - - ++iAtkSize; } } WRITE_SHORT(iAtkSize); for (int i = 0; i < iAtkSize; ++i) { - WRITE_SHORT(atkPlayerIdxs[i]); const AttackersTotals *atk = &atkTotals[i]; + WRITE_SHORT(atk->iUserID); WRITE_SHORT(static_cast(atk->dealtDmgs)); WRITE_SHORT(static_cast(atk->dealtHits)); WRITE_SHORT(static_cast(atk->takenDmgs)); @@ -3355,46 +3361,6 @@ bool CNEO_Player::ProcessTeamSwitchRequest(int iTeam) return true; } -int CNEO_Player::GetAttackersScores(const int attackerIdx) const -{ - if (NEORules()->GetGameType() == NEO_GAME_TYPE_DM || NEORules()->GetGameType() == NEO_GAME_TYPE_TDM) - { - return m_rfAttackersScores.Get(attackerIdx); - } - return m_rfAttackersScores.Get(attackerIdx); -} - -int CNEO_Player::GetAttackerHits(const int attackerIdx) const -{ - return m_rfAttackersHits.Get(attackerIdx); -} - -AttackersTotals CNEO_Player::GetAttackersTotals() const -{ - AttackersTotals totals = {}; - - const int thisIdx = entindex(); - for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) - { - if (pIdx == thisIdx) - { - continue; - } - - auto* neoAttacker = static_cast(UTIL_PlayerByIndex(pIdx)); - if (!neoAttacker || neoAttacker->IsHLTV()) - { - continue; - } - - totals.dealtDmgs += neoAttacker->GetAttackersScores(thisIdx); - totals.takenDmgs += GetAttackersScores(pIdx); - totals.dealtHits += neoAttacker->GetAttackerHits(thisIdx); - totals.takenHits += GetAttackerHits(pIdx); - } - return totals; -} - int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) { NEORules()->SetLastHurt(entindex()); @@ -3425,7 +3391,7 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) const float flFractionalDamage = info.GetDamage() - floor(info.GetDamage()); int iDamage = static_cast(info.GetDamage() - flFractionalDamage); - float flDmgAccumlator = m_rfAttackersAccumlator.Get(attackerIdx); + float flDmgAccumlator = m_rflAttackersAccumlator[attackerIdx]; flDmgAccumlator += flFractionalDamage; if (flDmgAccumlator >= 1.0f) { @@ -3463,9 +3429,9 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) // Apply damages/hits numbers if (iDamage > 0) { - m_rfAttackersScores.GetForModify(attackerIdx) += Min(iDamage, GetHealth()); - m_rfAttackersAccumlator.Set(attackerIdx, flDmgAccumlator); - m_rfAttackersHits.GetForModify(attackerIdx) += info.GetNumDamageEvents(); + m_riAttackersScores[attackerIdx] += Min(iDamage, GetHealth()); + m_rflAttackersAccumlator[attackerIdx] = flDmgAccumlator; + m_riAttackersHits[attackerIdx] += info.GetNumDamageEvents(); if (bIsTeamDmg && sv_neo_teamdamage_kick.GetBool() && NEORules()->IsRoundLive()) { diff --git a/src/game/server/neo/neo_player.h b/src/game/server/neo/neo_player.h index 60b67614c..7e5f7ac8a 100644 --- a/src/game/server/neo/neo_player.h +++ b/src/game/server/neo/neo_player.h @@ -226,13 +226,9 @@ class CNEO_Player : public CHL2MP_Player int ShouldTransmit( const CCheckTransmitInfo *pInfo) OVERRIDE; - int GetAttackersScores(const int attackerIdx) const; - int GetAttackerHits(const int attackerIdx) const; - void SetNameDupePos(const int dupePos); int NameDupePos() const; - AttackersTotals GetAttackersTotals() const; void StartShowDmgStats(const CTakeDamageInfo *info); void AddPoints(int score, bool bAllowNegativeScore, bool bIgnorePlayerTakeover = false); @@ -308,9 +304,9 @@ class CNEO_Player : public CHL2MP_Player CNetworkVar(float, m_flNextPingTime); // Used as 1-indexed, need MAX_PLAYERS_ARRAY_SAFE - CNetworkArray(int, m_rfAttackersScores, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(float, m_rfAttackersAccumlator, MAX_PLAYERS_ARRAY_SAFE); - CNetworkArray(int, m_rfAttackersHits, MAX_PLAYERS_ARRAY_SAFE); + int m_riAttackersScores[MAX_PLAYERS_ARRAY_SAFE]; + float m_rflAttackersAccumlator[MAX_PLAYERS_ARRAY_SAFE]; + int m_riAttackersHits[MAX_PLAYERS_ARRAY_SAFE]; CNetworkVar(unsigned char, m_NeoFlags); CNetworkString(m_szNeoName, MAX_PLAYER_NAME_LENGTH); diff --git a/src/game/shared/neo/neo_gamerules.cpp b/src/game/shared/neo/neo_gamerules.cpp index 30f905336..b6faeae68 100644 --- a/src/game/shared/neo/neo_gamerules.cpp +++ b/src/game/shared/neo/neo_gamerules.cpp @@ -3968,7 +3968,6 @@ void CNEORules::SetWinningTeam(int team, int iWinReason, bool bForceMapReset, bo m_bGotMatchWinner = gotMatchWinner; m_iMatchWinner = team; } -#endif // NEO JANK (nullsystem): Dont like how this is fetched twice (PlayerKilled, DeathNotice), // but blame the structure of the base classes @@ -3994,7 +3993,7 @@ static CNEO_Player* FetchAssists(CNEO_Player* attacker, CNEO_Player* victim) continue; } - const int assistDmg = victim->GetAttackersScores(assistIdx); + const int assistDmg = victim->m_riAttackersScores[assistIdx]; static const float MIN_DMG_QUALIFY_ASSIST = 0.5f; if ((float)assistDmg / victim->GetMaxHealth() >= MIN_DMG_QUALIFY_ASSIST) { @@ -4004,7 +4003,6 @@ static CNEO_Player* FetchAssists(CNEO_Player* attacker, CNEO_Player* victim) return NULL; } -#ifdef GAME_DLL void CNEORules::CheckIfCapPrevent(CNEO_Player *capPreventerPlayer) { if (!NEO_GAME_TYPE_SETTINGS[GetGameType()].capPrevent) @@ -4069,6 +4067,7 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) { BaseClass::PlayerKilled(pVictim, info); +#ifdef GAME_DLL auto attacker = dynamic_cast(info.GetAttacker()); auto victim = dynamic_cast(pVictim); auto grenade = dynamic_cast(info.GetInflictor()); @@ -4080,10 +4079,8 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) if (m_nRoundStatus == NeoRoundStatus::Pause) { -#ifdef GAME_DLL // Counter-act the death count for pausing state victim->IncrementDeathCount(-1); -#endif return; } @@ -4091,24 +4088,19 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) if (attacker == victim || (!attacker && !grenade)) { victim->AddPoints(-1, true); -#ifdef GAME_DLL CheckIfCapPrevent(victim); -#endif } -#ifdef GAME_DLL else if (!attacker && grenade && grenade->GetTeamNumber() == victim->GetTeamNumber()) { // Death by own team's grenade, but the player is already disconnected. Check for cap prevent. CheckIfCapPrevent(victim); } -#endif else if (attacker) { // Team kill if (IsTeamplay() && attacker->GetTeamNumber() == victim->GetTeamNumber()) { attacker->AddPoints(-1, true); -#ifdef GAME_DLL if (sv_neo_teamdamage_kick.GetBool() && IsRoundLive()) { ++attacker->m_iTeamKillsInflicted; @@ -4127,13 +4119,11 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) break; } } -#endif } // Enemy kill else { attacker->AddPoints(1, false); -#ifdef GAME_DLL if (GetGameType() == NEO_GAME_TYPE_JGR && IsRoundLive()) { if (attacker->GetClass() == NEO_CLASS_JUGGERNAUT) @@ -4152,7 +4142,6 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) } } } -#endif } } @@ -4177,6 +4166,7 @@ void CNEORules::PlayerKilled(CBasePlayer *pVictim, const CTakeDamageInfo &info) assister->AddPoints(1, false, true); } } +#endif // GAME_DLL } #ifdef GAME_DLL diff --git a/src/game/shared/neo/neo_player_shared.h b/src/game/shared/neo/neo_player_shared.h index a37a7b5d6..68efa8ed2 100644 --- a/src/game/shared/neo/neo_player_shared.h +++ b/src/game/shared/neo/neo_player_shared.h @@ -316,6 +316,7 @@ void UpdatePingCommands(CNEO_Player* player, const Vector& pingPos); struct AttackersTotals { + int iUserID; int dealtDmgs; int dealtHits; int takenDmgs; From 202df120ad818ddcd2aee545ff903a2ec5c53dee Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Fri, 24 Jul 2026 08:51:24 +0100 Subject: [PATCH 03/10] Rename variables for naming consistentcy, comment rewording Also remove unused += function from AttackersTotals --- .../client/neo/c_neo_killer_damage_infos.cpp | 30 +++++++++---------- src/game/client/neo/ui/neo_scoreboard.cpp | 8 ++--- src/game/server/neo/neo_player.cpp | 29 +++++++++--------- src/game/shared/neo/neo_player_shared.h | 16 +++------- 4 files changed, 38 insertions(+), 45 deletions(-) diff --git a/src/game/client/neo/c_neo_killer_damage_infos.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp index 27a0317f7..8c0ee5c7e 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -111,16 +111,16 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) NeoDamageReportClear(); AttackersTotals totals = {}; - // Read per-player damage stats from the message (authoritative server data) + // Read (server side) per-player damage stats const int iAtkSize = msg.ReadShort(); for (int i = 0; i < iAtkSize; ++i) { AttackersTotals *pDmgReport = &g_neoDamageReport[g_neoDamageReportSize]; pDmgReport->iUserID = msg.ReadShort(); - pDmgReport->dealtDmgs = msg.ReadShort(); - pDmgReport->dealtHits = msg.ReadShort(); - pDmgReport->takenDmgs = msg.ReadShort(); - pDmgReport->takenHits = msg.ReadShort(); + pDmgReport->iDealtDmgs = msg.ReadShort(); + pDmgReport->iDealtHits = msg.ReadShort(); + pDmgReport->iTakenDmgs = msg.ReadShort(); + pDmgReport->iTakenHits = msg.ReadShort(); auto *neoAttacker = USERID2NEOPLAYER(pDmgReport->iUserID); if (!neoAttacker || neoAttacker->IsHLTV()) @@ -137,31 +137,31 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); char infoLine[128] = {}; - if (pDmgReport->dealtDmgs > 0 && pDmgReport->dealtHits > 0) + if (pDmgReport->iDealtDmgs > 0 && pDmgReport->iDealtHits > 0) { V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", dmgerName, dmgerClass, - pDmgReport->dealtDmgs, pDmgReport->dealtHits); + pDmgReport->iDealtDmgs, pDmgReport->iDealtHits); ConMsg("%s", infoLine); - totals.takenDmgs += pDmgReport->dealtDmgs; - totals.takenHits += pDmgReport->dealtHits; + totals.iTakenDmgs += pDmgReport->iDealtDmgs; + totals.iTakenHits += pDmgReport->iDealtHits; } - if (pDmgReport->takenDmgs > 0 && pDmgReport->takenHits > 0) + if (pDmgReport->iTakenDmgs > 0 && pDmgReport->iTakenHits > 0) { V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", dmgerName, dmgerClass, - pDmgReport->takenDmgs, pDmgReport->takenHits); + pDmgReport->iTakenDmgs, pDmgReport->iTakenHits); ConMsg("%s", infoLine); - totals.dealtDmgs += pDmgReport->takenDmgs; - totals.dealtHits += pDmgReport->takenHits; + totals.iDealtDmgs += pDmgReport->iTakenDmgs; + totals.iDealtHits += pDmgReport->iTakenHits; } ++g_neoDamageReportSize; } ConMsg("Total damage dealt: %d in %d hits\nTotal damage received from players: %d in %d hits\n%s\n", - totals.dealtDmgs, totals.dealtHits, - totals.takenDmgs, totals.takenHits, + totals.iDealtDmgs, totals.iDealtHits, + totals.iTakenDmgs, totals.iTakenHits, BORDER); } USER_MESSAGE_REGISTER(KillerDamageInfo); diff --git a/src/game/client/neo/ui/neo_scoreboard.cpp b/src/game/client/neo/ui/neo_scoreboard.cpp index 85b0442ff..8b4fe7498 100644 --- a/src/game/client/neo/ui/neo_scoreboard.cpp +++ b/src/game/client/neo/ui/neo_scoreboard.cpp @@ -403,10 +403,10 @@ void CNEOScoreBoard::Update() } if (pDmgReport) { - pPlayerInfo->iDealtDmgs = pDmgReport->dealtDmgs; - pPlayerInfo->iDealtHits = pDmgReport->dealtHits; - pPlayerInfo->iTakenDmgs = pDmgReport->takenDmgs; - pPlayerInfo->iTakenHits = pDmgReport->takenHits; + pPlayerInfo->iDealtDmgs = pDmgReport->iDealtDmgs; + pPlayerInfo->iDealtHits = pDmgReport->iDealtHits; + pPlayerInfo->iTakenDmgs = pDmgReport->iTakenDmgs; + pPlayerInfo->iTakenHits = pDmgReport->iTakenHits; pPlayerInfo->bKilledYou = pNeoPlayer ? (pNeoPlayer->entindex() == g_neoKillerInfos.iEntIndex) : false; pPlayerInfo->bYouKilled = false; for (int j = 0; j < g_neoUserIDsLocalKilledSize; ++j) diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index e3f4f8c99..74203f696 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -2361,7 +2361,8 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) AttackersTotals atkTotals[MAX_PLAYERS_ARRAY_SAFE] = {}; int iAtkSize = 0; - // Send authoritative per-player damage stats directly from server + // Send server's per-player damage stats. This is the proper damage and + // hit count on player's death. const int thisIdx = entindex(); for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) { @@ -2376,19 +2377,19 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) continue; } - const int dealtDmgs = pNeoOther->m_riAttackersScores[thisIdx]; - const int dealtHits = pNeoOther->m_riAttackersHits[thisIdx]; - const int takenDmgs = m_riAttackersScores[pIdx]; - const int takenHits = m_riAttackersHits[pIdx]; + const int iDealtDmgs = pNeoOther->m_riAttackersScores[thisIdx]; + const int iDealtHits = pNeoOther->m_riAttackersHits[thisIdx]; + const int iTakenDmgs = m_riAttackersScores[pIdx]; + const int iTakenHits = m_riAttackersHits[pIdx]; - if ((dealtDmgs > 0 && dealtHits > 0) || (takenDmgs > 0 && takenHits > 0)) + if ((iDealtDmgs > 0 && iDealtHits > 0) || (iTakenDmgs > 0 && iTakenHits > 0)) { AttackersTotals *atk = &atkTotals[iAtkSize++]; atk->iUserID = pNeoOther->GetUserID(); - atk->dealtDmgs = dealtDmgs; - atk->dealtHits = dealtHits; - atk->takenDmgs = takenDmgs; - atk->takenHits = takenHits; + atk->iDealtDmgs = iDealtDmgs; + atk->iDealtHits = iDealtHits; + atk->iTakenDmgs = iTakenDmgs; + atk->iTakenHits = iTakenHits; } } @@ -2397,10 +2398,10 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) { const AttackersTotals *atk = &atkTotals[i]; WRITE_SHORT(atk->iUserID); - WRITE_SHORT(static_cast(atk->dealtDmgs)); - WRITE_SHORT(static_cast(atk->dealtHits)); - WRITE_SHORT(static_cast(atk->takenDmgs)); - WRITE_SHORT(static_cast(atk->takenHits)); + WRITE_SHORT(static_cast(atk->iDealtDmgs)); + WRITE_SHORT(static_cast(atk->iDealtHits)); + WRITE_SHORT(static_cast(atk->iTakenDmgs)); + WRITE_SHORT(static_cast(atk->iTakenHits)); } } MessageEnd(); diff --git a/src/game/shared/neo/neo_player_shared.h b/src/game/shared/neo/neo_player_shared.h index 68efa8ed2..0f028fe89 100644 --- a/src/game/shared/neo/neo_player_shared.h +++ b/src/game/shared/neo/neo_player_shared.h @@ -317,18 +317,10 @@ void UpdatePingCommands(CNEO_Player* player, const Vector& pingPos); struct AttackersTotals { int iUserID; - int dealtDmgs; - int dealtHits; - int takenDmgs; - int takenHits; - - void operator+=(const AttackersTotals &other) - { - dealtDmgs += other.dealtDmgs; - dealtHits += other.dealtHits; - takenDmgs += other.takenDmgs; - takenHits += other.takenHits; - } + int iDealtDmgs; + int iDealtHits; + int iTakenDmgs; + int iTakenHits; }; [[deprecated]] void KillerLineStr(char* killByLine, const int killByLineMax, From 16498327cef326a0efd302c885e4887fd974f95b Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Fri, 24 Jul 2026 08:59:18 +0100 Subject: [PATCH 04/10] short -> long for user ID --- src/game/client/neo/c_neo_killer_damage_infos.cpp | 2 +- src/game/server/neo/neo_player.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/client/neo/c_neo_killer_damage_infos.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp index 8c0ee5c7e..712eed5ab 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -116,7 +116,7 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) for (int i = 0; i < iAtkSize; ++i) { AttackersTotals *pDmgReport = &g_neoDamageReport[g_neoDamageReportSize]; - pDmgReport->iUserID = msg.ReadShort(); + pDmgReport->iUserID = msg.ReadLong(); pDmgReport->iDealtDmgs = msg.ReadShort(); pDmgReport->iDealtHits = msg.ReadShort(); pDmgReport->iTakenDmgs = msg.ReadShort(); diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 74203f696..5998a0072 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -2393,11 +2393,11 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) } } - WRITE_SHORT(iAtkSize); + WRITE_SHORT(static_cast(iAtkSize)); for (int i = 0; i < iAtkSize; ++i) { const AttackersTotals *atk = &atkTotals[i]; - WRITE_SHORT(atk->iUserID); + WRITE_LONG(atk->iUserID); WRITE_SHORT(static_cast(atk->iDealtDmgs)); WRITE_SHORT(static_cast(atk->iDealtHits)); WRITE_SHORT(static_cast(atk->iTakenDmgs)); From fa4dbddce9a720adad506c5a4b80802b45128f94 Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:42:19 +0100 Subject: [PATCH 05/10] guard loop index --- src/game/client/neo/c_neo_killer_damage_infos.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/neo/c_neo_killer_damage_infos.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp index 712eed5ab..f7346e544 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -112,7 +112,7 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) AttackersTotals totals = {}; // Read (server side) per-player damage stats - const int iAtkSize = msg.ReadShort(); + const int iAtkSize = Min(msg.ReadShort(), MAX_PLAYERS); for (int i = 0; i < iAtkSize; ++i) { AttackersTotals *pDmgReport = &g_neoDamageReport[g_neoDamageReportSize]; From 27fa03a6978271b61587234798e88b3186ff7a73 Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Fri, 24 Jul 2026 23:44:17 +0100 Subject: [PATCH 06/10] fixup total --- src/game/client/neo/c_neo_killer_damage_infos.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/game/client/neo/c_neo_killer_damage_infos.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp index f7346e544..b5b343d25 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -143,8 +143,8 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) dmgerName, dmgerClass, pDmgReport->iDealtDmgs, pDmgReport->iDealtHits); ConMsg("%s", infoLine); - totals.iTakenDmgs += pDmgReport->iDealtDmgs; - totals.iTakenHits += pDmgReport->iDealtHits; + totals.iDealtDmgs += pDmgReport->iDealtDmgs; + totals.iDealtHits += pDmgReport->iDealtHits; } if (pDmgReport->iTakenDmgs > 0 && pDmgReport->iTakenHits > 0) { @@ -152,8 +152,8 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) dmgerName, dmgerClass, pDmgReport->iTakenDmgs, pDmgReport->iTakenHits); ConMsg("%s", infoLine); - totals.iDealtDmgs += pDmgReport->iTakenDmgs; - totals.iDealtHits += pDmgReport->iTakenHits; + totals.iTakenDmgs += pDmgReport->iTakenDmgs; + totals.iTakenDmgs += pDmgReport->iTakenHits; } ++g_neoDamageReportSize; From 9ae5aa5853142b365f4d1680f838812a67799d29 Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Sat, 25 Jul 2026 11:55:17 +0100 Subject: [PATCH 07/10] Compact UserMessage bytes and handle extra in-case In CTG, it's improbable dmg+hits will go over 255 (unsigned char max) so therefore just fit it in a unsigned char/a byte instead. JGR and respawning (in a round) gamemodes could hit over 255 so fallback to short if it happens. Also another fallback is a 2nd UserMessage in case if it goes over the user message 255 bytes limit. Although in CTG it is also improbable it'll even reach the bytes limit. --- .../client/neo/c_neo_killer_damage_infos.cpp | 147 +++++++++++------- src/game/server/neo/neo_player.cpp | 64 +++++++- src/game/shared/hl2/hl2_usermessages.cpp | 1 + src/game/shared/neo/neo_player_shared.h | 11 ++ 4 files changed, 166 insertions(+), 57 deletions(-) diff --git a/src/game/client/neo/c_neo_killer_damage_infos.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp index b5b343d25..106f95568 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -11,6 +11,9 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +static inline AttackersTotals gTotals; +static const char *BORDER = "==========================\n"; + void NeoUserIDsLocalKilledClear() { g_neoUserIDsLocalKilledSize = 0; @@ -23,6 +26,87 @@ void NeoDamageReportClear() V_memset(g_neoDamageReport, 0, sizeof(g_neoDamageReport)); } +static ENEOCompactMsgFlag ReadDamageReport(bf_read &msg) +{ + // Read (server side) per-player damage stats + const int iAtkSize = Min(msg.ReadByte(), MAX_PLAYERS); + const ENEOCompactMsgFlag flags = static_cast(msg.ReadByte()); + + for (int i = 0; i < iAtkSize; ++i) + { + AttackersTotals *pDmgReport = &g_neoDamageReport[g_neoDamageReportSize]; + pDmgReport->iUserID = msg.ReadLong(); + + if (flags & NEO_COMPACT_MSG_FLAG_DMGS) + { + pDmgReport->iDealtDmgs = static_cast(msg.ReadByte()); + pDmgReport->iTakenDmgs = static_cast(msg.ReadByte()); + } + else + { + pDmgReport->iDealtDmgs = msg.ReadShort(); + pDmgReport->iTakenDmgs = msg.ReadShort(); + } + + if (flags & NEO_COMPACT_MSG_FLAG_HITS) + { + pDmgReport->iDealtHits = static_cast(msg.ReadByte()); + pDmgReport->iTakenHits = static_cast(msg.ReadByte()); + } + else + { + pDmgReport->iDealtHits = msg.ReadShort(); + pDmgReport->iTakenHits = msg.ReadShort(); + } + + auto *neoAttacker = USERID2NEOPLAYER(pDmgReport->iUserID); + if (!neoAttacker || neoAttacker->IsHLTV()) + { + continue; + } + + const char *dmgerName = neoAttacker->GetNeoPlayerName(); + if (!dmgerName) + { + continue; + } + + const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); + + char infoLine[128] = {}; + if (pDmgReport->iDealtDmgs > 0 && pDmgReport->iDealtHits > 0) + { + V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + pDmgReport->iDealtDmgs, pDmgReport->iDealtHits); + ConMsg("%s", infoLine); + gTotals.iDealtDmgs += pDmgReport->iDealtDmgs; + gTotals.iDealtHits += pDmgReport->iDealtHits; + } + if (pDmgReport->iTakenDmgs > 0 && pDmgReport->iTakenHits > 0) + { + V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", + dmgerName, dmgerClass, + pDmgReport->iTakenDmgs, pDmgReport->iTakenHits); + ConMsg("%s", infoLine); + gTotals.iTakenDmgs += pDmgReport->iTakenDmgs; + gTotals.iTakenHits += pDmgReport->iTakenHits; + } + + ++g_neoDamageReportSize; + } + + return flags; +} + +static void PrintTotalReport() +{ + ConMsg("Total damage dealt: %d in %d hits\nTotal damage received from players: %d in %d hits\n%s\n", + gTotals.iDealtDmgs, gTotals.iDealtHits, + gTotals.iTakenDmgs, gTotals.iTakenHits, + BORDER); +} + // Console + activation of damage info static void __MsgFunc_KillerDamageInfo(bf_read &msg) { @@ -43,7 +127,6 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) // Can't rely on Msg as it can print out of order, so do it in chunks static char killByLine[512]; - static const char *BORDER = "==========================\n"; bool setKillByLine = false; V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); @@ -109,60 +192,20 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) ConMsg("%sDamage infos (Round %d):\n%s\n", BORDER, NEORules()->roundNumber(), setKillByLine ? killByLine : ""); NeoDamageReportClear(); - AttackersTotals totals = {}; - - // Read (server side) per-player damage stats - const int iAtkSize = Min(msg.ReadShort(), MAX_PLAYERS); - for (int i = 0; i < iAtkSize; ++i) + gTotals = {}; + const ENEOCompactMsgFlag flags = ReadDamageReport(msg); + if (!(flags & NEO_COMPACT_MSG_FLAG_EXTRA)) { - AttackersTotals *pDmgReport = &g_neoDamageReport[g_neoDamageReportSize]; - pDmgReport->iUserID = msg.ReadLong(); - pDmgReport->iDealtDmgs = msg.ReadShort(); - pDmgReport->iDealtHits = msg.ReadShort(); - pDmgReport->iTakenDmgs = msg.ReadShort(); - pDmgReport->iTakenHits = msg.ReadShort(); - - auto *neoAttacker = USERID2NEOPLAYER(pDmgReport->iUserID); - if (!neoAttacker || neoAttacker->IsHLTV()) - { - continue; - } - - const char *dmgerName = neoAttacker->GetNeoPlayerName(); - if (!dmgerName) - { - continue; - } - - const char *dmgerClass = GetNeoClassName(neoAttacker->GetClass()); - - char infoLine[128] = {}; - if (pDmgReport->iDealtDmgs > 0 && pDmgReport->iDealtHits > 0) - { - V_sprintf_safe(infoLine, "Damage dealt to %s [%s]: %d in %d hits\n", - dmgerName, dmgerClass, - pDmgReport->iDealtDmgs, pDmgReport->iDealtHits); - ConMsg("%s", infoLine); - totals.iDealtDmgs += pDmgReport->iDealtDmgs; - totals.iDealtHits += pDmgReport->iDealtHits; - } - if (pDmgReport->iTakenDmgs > 0 && pDmgReport->iTakenHits > 0) - { - V_sprintf_safe(infoLine, "Damage taken from %s [%s]: %d in %d hits\n", - dmgerName, dmgerClass, - pDmgReport->iTakenDmgs, pDmgReport->iTakenHits); - ConMsg("%s", infoLine); - totals.iTakenDmgs += pDmgReport->iTakenDmgs; - totals.iTakenDmgs += pDmgReport->iTakenHits; - } - - ++g_neoDamageReportSize; + PrintTotalReport(); } +} - ConMsg("Total damage dealt: %d in %d hits\nTotal damage received from players: %d in %d hits\n%s\n", - totals.iDealtDmgs, totals.iDealtHits, - totals.iTakenDmgs, totals.iTakenHits, - BORDER); +static void __MsgFunc_KillerDamageInfoExtra(bf_read &msg) +{ + ReadDamageReport(msg); + PrintTotalReport(); } + USER_MESSAGE_REGISTER(KillerDamageInfo); +USER_MESSAGE_REGISTER(KillerDamageInfoExtra); diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 5998a0072..43c031aac 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -2360,6 +2360,8 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) AttackersTotals atkTotals[MAX_PLAYERS_ARRAY_SAFE] = {}; int iAtkSize = 0; + int iMaxDmgs = 0; + int iMaxHits = 0; // Send server's per-player damage stats. This is the proper damage and // hit count on player's death. @@ -2390,18 +2392,70 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) atk->iDealtHits = iDealtHits; atk->iTakenDmgs = iTakenDmgs; atk->iTakenHits = iTakenHits; + + iMaxDmgs = Max(iMaxDmgs, Max(iTakenDmgs, iDealtDmgs)); + iMaxHits = Max(iMaxHits, Max(iTakenHits, iDealtHits)); } } - WRITE_SHORT(static_cast(iAtkSize)); + // CTG will never hit more than 255 per person, and improbable for hits per person + // But for juggernaut or respawns in a round, this can happen + ENEOCompactMsgFlag flags = 0; + if (iMaxDmgs <= UCHAR_MAX) flags |= NEO_COMPACT_MSG_FLAG_DMGS; + if (iMaxHits <= UCHAR_MAX) flags |= NEO_COMPACT_MSG_FLAG_HITS; + + const int iWriteSize = 2 + (V_strlen(killedWithName) + 1) + 1 + 1; + + int iDmgInfoWriteSize = 4; + iDmgInfoWriteSize += (flags & NEO_COMPACT_MSG_FLAG_DMGS) ? 2 : 4; + iDmgInfoWriteSize += (flags & NEO_COMPACT_MSG_FLAG_HITS) ? 2 : 4; + + // Improbable it'll happen but just in-case + int iAtkFirstSize = iAtkSize; + if ((iWriteSize + (iAtkSize * iDmgInfoWriteSize)) > MAX_USER_MSG_DATA) + { + const int iFreeSpace = MAX_USER_MSG_DATA - iWriteSize; + iAtkFirstSize = iFreeSpace / iDmgInfoWriteSize; + flags |= NEO_COMPACT_MSG_FLAG_EXTRA; + } + + // MAX_PLAYERS fits in a byte + WRITE_BYTE(static_cast(iAtkFirstSize)); + WRITE_BYTE(flags); + for (int i = 0; i < iAtkSize; ++i) { + if (i >= iAtkFirstSize) + { + MessageEnd(); + UserMessageBegin(filter, "KillerDamageInfoExtra"); + WRITE_BYTE(static_cast(iAtkSize - iAtkFirstSize)); + WRITE_BYTE(flags); + } + const AttackersTotals *atk = &atkTotals[i]; WRITE_LONG(atk->iUserID); - WRITE_SHORT(static_cast(atk->iDealtDmgs)); - WRITE_SHORT(static_cast(atk->iDealtHits)); - WRITE_SHORT(static_cast(atk->iTakenDmgs)); - WRITE_SHORT(static_cast(atk->iTakenHits)); + if (flags & NEO_COMPACT_MSG_FLAG_DMGS) + { + WRITE_BYTE(static_cast(atk->iDealtDmgs)); + WRITE_BYTE(static_cast(atk->iTakenDmgs)); + } + else + { + WRITE_SHORT(static_cast(atk->iDealtDmgs)); + WRITE_SHORT(static_cast(atk->iTakenDmgs)); + } + + if (flags & NEO_COMPACT_MSG_FLAG_HITS) + { + WRITE_BYTE(static_cast(atk->iDealtHits)); + WRITE_BYTE(static_cast(atk->iTakenHits)); + } + else + { + WRITE_SHORT(static_cast(atk->iDealtHits)); + WRITE_SHORT(static_cast(atk->iTakenHits)); + } } } MessageEnd(); diff --git a/src/game/shared/hl2/hl2_usermessages.cpp b/src/game/shared/hl2/hl2_usermessages.cpp index a02951b3f..1c06c8976 100644 --- a/src/game/shared/hl2/hl2_usermessages.cpp +++ b/src/game/shared/hl2/hl2_usermessages.cpp @@ -49,6 +49,7 @@ void RegisterUserMessages( void ) #ifdef NEO usermessages->Register( "KillerDamageInfo", -1 ); + usermessages->Register( "KillerDamageInfoExtra", -1 ); usermessages->Register( "IdleRespawnShowMenu", -1 ); usermessages->Register( "CSpectatorTakeoverPlayer", -1 ); usermessages->Register( "AchievementMark", -1 ); diff --git a/src/game/shared/neo/neo_player_shared.h b/src/game/shared/neo/neo_player_shared.h index 0f028fe89..a0368e340 100644 --- a/src/game/shared/neo/neo_player_shared.h +++ b/src/game/shared/neo/neo_player_shared.h @@ -323,6 +323,17 @@ struct AttackersTotals int iTakenHits; }; +enum ENEOCompactMsgFlag_ : unsigned char +{ + NEO_COMPACT_MSG_FLAG_NIL = 0, + NEO_COMPACT_MSG_FLAG_DMGS = (1 << 0), + NEO_COMPACT_MSG_FLAG_HITS = (1 << 1), + NEO_COMPACT_MSG_FLAG_EXTRA = (1 << 2), +}; + +typedef unsigned char ENEOCompactMsgFlag; + + [[deprecated]] void KillerLineStr(char* killByLine, const int killByLineMax, CNEO_Player* neoAttacker, const CNEO_Player* neoVictim, const char* killedWith = ""); From 5e93ecceb59b0afd4d64adc8676db326a460a525 Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Sat, 25 Jul 2026 14:00:08 +0100 Subject: [PATCH 08/10] Fixup for dmg/hit stats for bot takeover/impersonation It will clear and record from when the takeover starts, the stats will not include the bot's own session during the round. --- .../client/neo/c_neo_killer_damage_infos.cpp | 14 +++++++- .../client/neo/c_neo_killer_damage_infos.h | 3 ++ src/game/client/neo/c_neo_player.cpp | 5 ++- .../client/neo/ui/neo_hud_killer_info.cpp | 2 +- src/game/server/neo/neo_player.cpp | 32 ++++++++++++++++--- 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/game/client/neo/c_neo_killer_damage_infos.cpp b/src/game/client/neo/c_neo_killer_damage_infos.cpp index 106f95568..a8c183ce8 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.cpp +++ b/src/game/client/neo/c_neo_killer_damage_infos.cpp @@ -26,6 +26,18 @@ void NeoDamageReportClear() V_memset(g_neoDamageReport, 0, sizeof(g_neoDamageReport)); } +void NeoKillerInfoClear() +{ + V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); +} + +void NeoAllKDReportsClear() +{ + NeoDamageReportClear(); + NeoUserIDsLocalKilledClear(); + NeoKillerInfoClear(); +} + static ENEOCompactMsgFlag ReadDamageReport(bf_read &msg) { // Read (server side) per-player damage stats @@ -129,7 +141,7 @@ static void __MsgFunc_KillerDamageInfo(bf_read &msg) bool setKillByLine = false; - V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + NeoKillerInfoClear(); g_neoKillerInfos.bHasDmgInfos = true; if (killerIdx > 0 && killerIdx <= gpGlobals->maxClients) diff --git a/src/game/client/neo/c_neo_killer_damage_infos.h b/src/game/client/neo/c_neo_killer_damage_infos.h index 336286fc7..cbabf40de 100644 --- a/src/game/client/neo/c_neo_killer_damage_infos.h +++ b/src/game/client/neo/c_neo_killer_damage_infos.h @@ -18,6 +18,9 @@ struct CNEOKillerInfos void NeoUserIDsLocalKilledClear(); void NeoDamageReportClear(); +void NeoKillerInfoClear(); + +void NeoAllKDReportsClear(); // Global instance of CNEOKillerInfos and local-player's kill record inline CNEOKillerInfos g_neoKillerInfos; diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index 973e710dd..e75e69b61 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -1996,6 +1996,7 @@ void __MsgFunc_CSpectatorTakeoverPlayer(bf_read &msg) // Save for later in C_NEO_Player::OnDataChanged pSpectatorTakingOver->m_hSpectatorTakeoverPlayerTarget = pPlayerTakeoverTarget; pSpectatorTakingOver->m_bCopyOverTakeoverPlayerDetails = true; + NeoAllKDReportsClear(); } } @@ -2141,8 +2142,6 @@ void C_NEO_Player::ClearLocalPlayerDmgReports() { if (IsLocalPlayer()) { - NeoDamageReportClear(); - NeoUserIDsLocalKilledClear(); - V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + NeoAllKDReportsClear(); } } diff --git a/src/game/client/neo/ui/neo_hud_killer_info.cpp b/src/game/client/neo/ui/neo_hud_killer_info.cpp index 9bf7d99f4..1da827597 100644 --- a/src/game/client/neo/ui/neo_hud_killer_info.cpp +++ b/src/game/client/neo/ui/neo_hud_killer_info.cpp @@ -58,7 +58,7 @@ void CNEOHud_KillerInfo::CommonSetupHUD() void CNEOHud_KillerInfo::resetHUDState() { - V_memset(&g_neoKillerInfos, 0, sizeof(CNEOKillerInfos)); + NeoKillerInfoClear(); } void CNEOHud_KillerInfo::UpdateStateForNeoHudElementDraw() diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 43c031aac..a7063a389 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -3439,6 +3439,7 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) if (auto *attacker = ToNEOPlayer(info.GetAttacker())) { CNEO_Player* pImpersonated = attacker->GetSpectatorTakeoverPlayerTarget(); + const int attackerRecIdx = attacker->entindex(); // Record goes to the impersonator's original index const int attackerIdx = pImpersonated ? pImpersonated->entindex() : attacker->entindex(); NEORules()->SetLastAttacker(entindex()); // NEO TODO (Adam) Once we can spectate non-players, let last attacker be non-neoplayer (Jeff) @@ -3446,7 +3447,7 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) const float flFractionalDamage = info.GetDamage() - floor(info.GetDamage()); int iDamage = static_cast(info.GetDamage() - flFractionalDamage); - float flDmgAccumlator = m_rflAttackersAccumlator[attackerIdx]; + float flDmgAccumlator = m_rflAttackersAccumlator[attackerRecIdx]; flDmgAccumlator += flFractionalDamage; if (flDmgAccumlator >= 1.0f) { @@ -3484,9 +3485,9 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) // Apply damages/hits numbers if (iDamage > 0) { - m_riAttackersScores[attackerIdx] += Min(iDamage, GetHealth()); - m_rflAttackersAccumlator[attackerIdx] = flDmgAccumlator; - m_riAttackersHits[attackerIdx] += info.GetNumDamageEvents(); + m_riAttackersScores[attackerRecIdx] += Min(iDamage, GetHealth()); + m_rflAttackersAccumlator[attackerRecIdx] = flDmgAccumlator; + m_riAttackersHits[attackerRecIdx] += info.GetNumDamageEvents(); if (bIsTeamDmg && sv_neo_teamdamage_kick.GetBool() && NEORules()->IsRoundLive()) { @@ -4359,6 +4360,29 @@ void CNEO_Player::SpectatorTakeoverPlayerPreThink() m_bInVision = pPlayerTakeoverTarget->m_bInVision; m_nVisionLastTick = pPlayerTakeoverTarget->m_nVisionLastTick; + // Just clear this so the attackers scores/hits are based on only when it's + // impersonated not including the bot controlled part + const int thisIdx = entindex(); + V_memset(m_riAttackersScores, 0, sizeof(m_riAttackersScores)); + V_memset(m_rflAttackersAccumlator, 0, sizeof(m_rflAttackersAccumlator)); + V_memset(m_riAttackersHits, 0, sizeof(m_riAttackersHits)); + for (int pIdx = 1; pIdx <= gpGlobals->maxClients; ++pIdx) + { + if (pIdx == thisIdx) + { + continue; + } + + auto *pNeoOther = static_cast(UTIL_PlayerByIndex(pIdx)); + if (!pNeoOther || pNeoOther->IsHLTV()) + { + continue; + } + + pNeoOther->m_riAttackersScores[thisIdx] = 0; + pNeoOther->m_rflAttackersAccumlator[thisIdx] = 0.0f; + pNeoOther->m_riAttackersHits[thisIdx] = 0; + } // Transfer weapons from the takeover target. RemoveAllItems(false); From 1feb702ad2c549942f86d4e0c8a62b22f74d4355 Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:29:07 +0100 Subject: [PATCH 09/10] fixup clear --- src/game/client/neo/c_neo_player.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index e75e69b61..0b6563fae 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -1996,7 +1996,10 @@ void __MsgFunc_CSpectatorTakeoverPlayer(bf_read &msg) // Save for later in C_NEO_Player::OnDataChanged pSpectatorTakingOver->m_hSpectatorTakeoverPlayerTarget = pPlayerTakeoverTarget; pSpectatorTakingOver->m_bCopyOverTakeoverPlayerDetails = true; - NeoAllKDReportsClear(); + if (pSpectatorTakingOver->IsLocalPlayer()) + { + NeoAllKDReportsClear(); + } } } From 9296589319c66be605dc1174d9d910e215508680 Mon Sep 17 00:00:00 2001 From: nullsystem <15316579+nullsystem@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:46:04 +0100 Subject: [PATCH 10/10] fixup comparison --- src/game/server/neo/neo_player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index a7063a389..7e04b45bc 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -2425,7 +2425,7 @@ void CNEO_Player::StartShowDmgStats(const CTakeDamageInfo *info) for (int i = 0; i < iAtkSize; ++i) { - if (i >= iAtkFirstSize) + if (i == iAtkFirstSize) { MessageEnd(); UserMessageBegin(filter, "KillerDamageInfoExtra");