MP match forensics: per-peer matchlog + relay auto-upload + matchcheck
For the 8-player playtest. Damage authority is VICTIM-side, so no single peer sees the whole match: every -net instance now writes a compact machine-parseable matchlog_<date>_<time>_<pid>.txt (auto-armed by -net; BT_MATCHLOG=1/0 overrides; solo silent), and at mission end a relay-mode pod dials the relay game port (the seat-request throwaway-dial pattern, envelope route -9) and streams the file back -- the relay saves every peer's copy under matchlogs/ on the operator's machine, so testers send nothing by hand. tools/matchcheck.py <dir> reconciles all of them: damage claimed (FIRE/PROJ/SPLASH/RAM, shooter-side) vs applied (DMG, victim-side authoritative), kill/death replication across observers, PLAYER_DEAD counts, scores, version skew, replicant-application and unattributed-damage anomalies, mid-mission disconnects. Hooks at the authoritative sites: Mech::TakeDamageMessageHandler (DMG, post-base, zone + post-application damageLevel), MechWeapon:: SendDamageMessage (FIRE), projectile impact/splash/collision dispatch (PROJ/SPLASH/RAM), wreck entry (DEATH -- a ONE-SHOT latch at MovementMode 9, NOT the transition: a replicant arrives at 9 straight from the type-6 record header and never runs the transition branch), BTPlayer vehicle/death/score handlers (VEHICLE/PLAYER_DEAD/SCORE), zone crit cascade (CRIT), APP.cpp RunningMission (MISSION), L4NET host handlers (PEER_UP/PEER_DOWN). Every line t=/w=/st=-stamped + fflushed. Verified 2-node: mesh run pairs A's 22 FIRE 1:1 with B's 22 DMG (same amounts, cylinder-resolved zones, ~200ms latency); force-damage kill run logs 103 DMG + 3 DEATH inst=M + 3 PLAYER_DEAD across three respawn cycles victim-side and the kill SCOREs shooter-side; relay-mode run auto-uploaded BOTH peers' files at the mission-clock stop and matchcheck produced the full report with exactly one (correct) anomaly -- the force hook's claim-less damage, the unattributed-damage detector working. Relay gained a route-specific 8MB cap for the upload frame (game frames stay capped at 1600). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
3097d269b4
commit
753540de96
@@ -70,6 +70,7 @@
|
||||
//
|
||||
|
||||
#include <bt.hpp>
|
||||
#include <matchlog.hpp> // MP match forensics (VEHICLE/PLAYER_DEAD/SCORE)
|
||||
#pragma hdrstop
|
||||
|
||||
#if !defined(BTPLAYER_HPP)
|
||||
@@ -414,6 +415,12 @@ void
|
||||
//
|
||||
++deathCount; // this+0x200
|
||||
message->deathCount = deathCount; // message+0x38
|
||||
|
||||
// MP MATCH FORENSICS (matchlog.hpp): the owning player's death
|
||||
// notification (fires exactly once per death, deduped above).
|
||||
if (BTMatchLogActive())
|
||||
BTMatchLog("PLAYER_DEAD", "player=%d:%d deaths=%d",
|
||||
BTMatchHostOf(GetEntityID()), (int)GetEntityID(), deathCount);
|
||||
// TODO(bring-up): scenarioRole is the scoring role looked up from the mission's
|
||||
// role registry (btplayer.cpp:815, currently deferred), so it is NULL for the
|
||||
// minimal dev missions. Only debit a life when a role is present.
|
||||
@@ -521,6 +528,14 @@ void
|
||||
//
|
||||
currentScore +=
|
||||
(MECH_TONNAGE(target_mech) / MECH_TONNAGE(our_mech)) * award; // (+0x4bc / +0x4bc), this+0x278
|
||||
|
||||
// MP MATCH FORENSICS (matchlog.hpp): inflicted-damage score (type=0),
|
||||
// logged with the post-accumulate total.
|
||||
if (BTMatchLogActive())
|
||||
BTMatchLog("SCORE",
|
||||
"player=%d:%d type=0 award=%.2f total=%.2f kills=%d",
|
||||
BTMatchHostOf(GetEntityID()), (int)GetEntityID(),
|
||||
(float)award, (float)currentScore, killCount);
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -712,6 +727,17 @@ void
|
||||
// straggler score lands the award without the vehicle response.
|
||||
//
|
||||
message->scoreAward = award; // msg+0x1c
|
||||
|
||||
// MP MATCH FORENSICS (matchlog.hpp): every folded score award. total is
|
||||
// the score BEFORE this award lands (the base applies it right after) --
|
||||
// matchcheck sums the awards; kills counts ride along.
|
||||
if (BTMatchLogActive())
|
||||
BTMatchLog("SCORE",
|
||||
"player=%d:%d type=%d award=%.2f total=%.2f kills=%d",
|
||||
BTMatchHostOf(GetEntityID()), (int)GetEntityID(),
|
||||
(int)message->scoreType, (float)award, (float)currentScore,
|
||||
killCount);
|
||||
|
||||
if (playerVehicle == 0)
|
||||
{
|
||||
currentScore += message->scoreAward; // the base's award apply
|
||||
@@ -920,6 +946,20 @@ void
|
||||
Register_Object(playerVehicle);
|
||||
}
|
||||
|
||||
// MP MATCH FORENSICS (matchlog.hpp): the player has a vehicle (initial
|
||||
// spawn AND every drop-zone respawn route here) -- the line that binds a
|
||||
// player id to a mech id for the whole analysis.
|
||||
if (BTMatchLogActive() && playerVehicle != 0)
|
||||
BTMatchLog("VEHICLE",
|
||||
"player=%d:%d mech=%d:%d deaths=%d pos=%.1f,%.1f,%.1f",
|
||||
BTMatchHostOf(GetEntityID()), (int)GetEntityID(),
|
||||
BTMatchHostOf(playerVehicle->GetEntityID()),
|
||||
(int)playerVehicle->GetEntityID(),
|
||||
deathCount,
|
||||
(float)mech_location.linearPosition.x,
|
||||
(float)mech_location.linearPosition.y,
|
||||
(float)mech_location.linearPosition.z);
|
||||
|
||||
// --- BRING-UP: spawn a stationary target/enemy mech (gameplay scaffolding) ---
|
||||
// The mission system is networked (one pilot per host) and BT's bot/AI code
|
||||
// is absent, so a solo mission has nothing to fight. When BT_SPAWN_ENEMY is
|
||||
|
||||
Reference in New Issue
Block a user