scoring: land inflicted credit on the OWNER's machine (Steam + console safe)
Completes 2772175/e82f54c. The interceptor restored the credit; this puts it
on the right node, so a player's score accumulates again.
The operator corrected two of my claims, and both were load-bearing:
1. Scores DID accumulate before build 787. Checked: build 774 already had
`currentScore = 0` in the console flush, so the flush was never eating
score. My "score zeroed every interval" theory is dropped. The watermark
from e82f54c stays only because it is harmless and keeps a master's own
total intact across a flush -- it was not fixing a field bug.
2. The reroute works, and 774's own comment says so: the killer's player is a
REPLICANT, so Entity::Dispatch reroutes to the owning host
(ENTITY.cpp:244-251) and the credit lands on the killer's OWN machine.
That is how kill credit has always crossed nodes.
So the earlier master-only gate was the right idea and failed for a reason I
guessed wrong. A rerouted message arrives over the WIRE through Receive(),
which goes straight to the handler table -- the virtual Dispatch override is
never called on the receiving side. Type 0 therefore landed in
ScoreMessageHandler's arm, which Verify-rejected it: award 0.00.
Fix is both halves:
* Dispatch intercepts on a MASTER only -- local delivery stays exactly as
@004bffa0 does it;
* ScoreMessageHandler's type-0 arm DELEGATES to ScoreInflictedMessageHandler
instead of Verify-rejecting -- wire delivery gets the same handler.
One accumulator, on the machine that owns the score.
Works for Steam today (no console tally exists -- btconsole.py/btoperator.py
handle no score at all) AND for a real operator console later: the console
flush is untouched and still ships authentic deltas under the owner's ownerID.
Benched (cross-node zone-walk kill):
credit node shooter's master only (victim's node banks 0)
running total 253.60 and CLIMBING, no resets
(was: peaks ~35, snapping back every few seconds)
type-0 rejects 0
LOOSE END: each real award is followed by a duplicate row with award=0.00
(80 real + 80 zero). Harmless -- the total is unaffected -- but it means the
report is delivered twice on the owner; not yet explained.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCJQkvq6G2JNrpVbA75tVZ
This commit is contained in:
co-authored by
Claude Opus 5
parent
e82f54c957
commit
1324c81719
@@ -810,7 +810,20 @@ void
|
||||
&& what->messageID == Player::ScoreMessageID
|
||||
&& ((BTPlayer::ScoreMessage *)what)->scoreType
|
||||
== BTPlayer::ScoreMessage::DamageInflictedScore
|
||||
)
|
||||
//
|
||||
// MASTER ONLY. The binary intercepts unconditionally because its
|
||||
// +0x278 is a console DELTA -- every node's contribution is flushed
|
||||
// under the scoring player's ownerID and the CONSOLE totals it, so the
|
||||
// computing node is irrelevant. Our port has no console tally
|
||||
// (btconsole.py/btoperator.py handle no score at all) and reads +0x278
|
||||
// on the OWNING node for the SCORE gauge, CalcRanking and the replicated
|
||||
// Player__UpdateRecord. Banking on the victim's replicant copy
|
||||
// therefore loses the credit to the master's next update record.
|
||||
// Falling through lets Entity::Dispatch reroute to the owner, where the
|
||||
// wire delivery lands in ScoreMessageHandler's type-0 arm (which now
|
||||
// delegates back to the inflicted handler).
|
||||
//
|
||||
&& GetInstance() == Entity::MasterInstance)
|
||||
{
|
||||
ScoreInflictedMessageHandler((BTPlayer::ScoreMessage *)what);
|
||||
return;
|
||||
@@ -987,15 +1000,26 @@ void
|
||||
{
|
||||
case BTPlayer::ScoreMessage::DamageInflictedScore: // 0
|
||||
//
|
||||
// Inflicted-damage messages belong to ScoreInflictedMessageHandler.
|
||||
// Inflicted-damage messages belong to ScoreInflictedMessageHandler, and
|
||||
// the binary's Dispatch override (@004bffa0) guarantees they never reach
|
||||
// here -- which is why the original arm is a bare Verify.
|
||||
//
|
||||
Verify(
|
||||
False,
|
||||
"BTPlayer::ScoreMessageHandler should not be "
|
||||
"given DamageInflictedScoreMessages!", // @0051324a
|
||||
"d:\\tesla_bt\\bt\\btplayer.cpp", // @0051329a
|
||||
0x296
|
||||
);
|
||||
// PORT DIVERGENCE (2026-08-07), and it is a DELIVERY-PATH difference,
|
||||
// not a scoring one. Damage is applied on the VICTIM's node, so block B
|
||||
// dispatches the inflicted report to the SHOOTER's player object there --
|
||||
// a REPLICANT -- and Entity::Dispatch reroutes it to the owning host so
|
||||
// the credit lands on the shooter's OWN machine (the same reroute that
|
||||
// carries kill credit, ENTITY.cpp:244-251). But a message arriving over
|
||||
// the WIRE is delivered through Receive(), straight to this handler
|
||||
// table: the virtual Dispatch override is never called on the receiving
|
||||
// side. So the rerouted report lands HERE, and Verify-rejecting it
|
||||
// threw away every cross-node inflicted credit (benched: award=0.00).
|
||||
//
|
||||
// Delegate instead. Local deliveries are still intercepted by Dispatch
|
||||
// exactly as the binary does; wire deliveries land here and get the same
|
||||
// handler. One accumulator, on the machine that owns the score.
|
||||
//
|
||||
ScoreInflictedMessageHandler(message);
|
||||
break;
|
||||
|
||||
case BTPlayer::ScoreMessage::DamageReceivedScore: // 1
|
||||
|
||||
Reference in New Issue
Block a user