MP cross-pod damage ROOT-CAUSED: EntityID localID corrupted by hostID on the wire (task #47)
Traced the dispatched-message delivery end to end with BT_MP_NET / BT_MP_FORCE_DMG. Everything works except one wire step: - A's Entity::Dispatch reroutes the replicant's TakeDamage (application->SendMessage(ownerID=3, EntityManager, msg)); POST-Dispatch the message carries entityID=3:22 (host:local) -- the replicant's own ID, matching B's master (GetEntityID()==3:22). VERIFIED sent. - On B the message ARRIVES, GetEntityPointer finds an entity, Posts it, the event drains (ProcessEventTask = ProcessOneEvent(0)), Event::Process runs, Receive finds+calls a handler. VERIFIED the full deliver chain. - BUG: B receives entityID=3:19, NOT 3:22 -- the localID dropped by exactly the hostID (3) between A's send and B's receive. So GetEntityPointer(3:19) returns the WRONG entity (classID 48, not the mech 0xBB9), whose base handler ignores the unaimed hit -> 0 damage. Auto-replicated UPDATE records (msgID 18) arrive with the correct 3:22 and find the mech, so the corruption is specific to the dispatched- message wire path/direction. Next: the host-relative EntityID (de)serialization on the dispatched-msg path (RoutePacket / packet EntityID encoding) -- diff vs the update path which translates correctly. Diagnostics retained (all BT_MP_NET-gated, off in solo -- verified: solo 22 hits, 0 probe noise). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a9c3e96f20
commit
c78662a0f1
+25
-13
@@ -62,19 +62,31 @@ the solo `gEnemyMech` to the picked `hotTarget` (missile impact + projectile val
|
|||||||
generalised via `BTIsRegisteredMech`). Verified one-box: instance A correctly enumerates B's
|
generalised via `BTIsRegisteredMech`). Verified one-box: instance A correctly enumerates B's
|
||||||
mech as a live `ReplicantInstance` (inst=4), 20 zones, `ownerID=3`, alive. Solo un-regressed
|
mech as a live `ReplicantInstance` (inst=4), 20 zones, `ownerID=3`, alive. Solo un-regressed
|
||||||
(pick→damage→kill chain clean).
|
(pick→damage→kill chain clean).
|
||||||
**Step 2 — cross-pod damage: dispatch + reroute CORRECT, wire delivery is the GAP.** The engine
|
**Step 2 — cross-pod damage: ROOT-CAUSED to an EntityID wire-corruption (task #47, full trace).**
|
||||||
`Entity::Dispatch` (ENTITY.cpp:244) DOES reroute a replicant's message:
|
The whole chain works EXCEPT one wire step; traced end-to-end with `BT_MP_NET`/`BT_MP_FORCE_DMG`:
|
||||||
`if (GetInstance()==ReplicantInstance) application->SendMessage(ownerID, EntityManagerClientID,
|
1. `Entity::Dispatch` (ENTITY.cpp:244) reroutes a replicant's msg — `if(ReplicantInstance)
|
||||||
message)`. Verified (`BT_MP_FORCE_DMG`): A dispatches TakeDamage at B's replicant with a VALID
|
application->SendMessage(ownerID, EntityManagerClientID, msg)` → `NetworkManager::Send`
|
||||||
`ownerID=3` → the reroute is invoked. BUT B's master takes **0** damage (no STEP-6 `unaimed
|
(L4NET.CPP:1062) → `send()`. VERIFIED: A dispatches TakeDamage (msgID 21) at B's replicant
|
||||||
hit`), and the message resolves nowhere on A either. So the reroute FIRES but the dispatched
|
(`ownerID=3`), and POST-Dispatch the message carries `entityID=3:22` (host:local) — the
|
||||||
entity-message never reaches the owning master. ⚠ Corrects the earlier [T3] "Dispatch already
|
replicant's own ID, matching B's master (`m->GetEntityID()==3:22`).
|
||||||
reroutes" to: reroute code exists + is invoked with a valid owner; the break is DOWNSTREAM in the
|
2. On B: `EntityManager::ReceiveNetworkPacket` (NTTMGR.cpp:107) → `GetEntityPointer(entityID)` →
|
||||||
transport/delivery of a *dispatched* message (vs the auto-replicated update records, which flow —
|
`Post(EntityManagerEventPriority, entity, msg)` → event drains (ProcessEventTask =
|
||||||
`net-rx` works both ways). ⚠ `[net-tx]` trace tag is NOT wired on the send path (logs 0 even
|
`ProcessOneEvent(0)`, all priorities) → `Event::Process` → `entity->Receive(msg)` →
|
||||||
though rx proves sends happen) — instrument the actual `Application::SendMessage`→`RoutePacket`→
|
`activeMessageHandlers->Find(21)` → handler. VERIFIED: the message ARRIVES, is Posted,
|
||||||
L4NET send + B-side `EntityManager` receive/deliver to localise. Diagnostics: `BT_MP_LOG`
|
Processed, and a handler is Found+called.
|
||||||
(candidate dump), `BT_MP_FORCE_DMG` (direct replicant-dispatch proof hook).
|
3. ⚠ **THE BUG:** B receives the message with `entityID=3:19`, NOT `3:22` — the localID dropped by
|
||||||
|
exactly the **hostID (3)** somewhere between A's `send()` (3:22) and B's receive (3:19). So
|
||||||
|
`GetEntityPointer(3:19)` returns the WRONG entity (a **classID 48**, not the mech 0xBB9), whose
|
||||||
|
base `Entity::TakeDamageMessageHandler` ignores the unaimed (zone −1) hit → B's mech takes 0
|
||||||
|
damage. The AUTO-REPLICATED UPDATE records (msgID 18) arrive with the CORRECT `3:22` and find
|
||||||
|
the mech — so the corruption is specific to the DISPATCHED-message path/direction, not the
|
||||||
|
receive lookup itself.
|
||||||
|
**Next:** find the host-relative EntityID (de)serialization that subtracts/adds the hostID on the
|
||||||
|
dispatched-message wire path (RoutePacket / the packet EntityID encoding) — the update path shows
|
||||||
|
the correct translation, so diff the two. Diagnostics retained (all `BT_MP_NET`-gated, off in
|
||||||
|
solo — verified): `[mp-force]` (pre/post-Dispatch entityID), `[mp-send]`/`[mp-recv]` (wire +
|
||||||
|
classID), `[mp-disp]` (reroute branch), `[mp-evt]`/`[mp-recv2]`/`[mp-hdlr]` (deliver chain),
|
||||||
|
`BT_MP_LOG` (candidate dump).
|
||||||
|
|
||||||
## Remaining (P6 phase 4 / Phase 7)
|
## Remaining (P6 phase 4 / Phase 7)
|
||||||
Cross-pod damage WIRE DELIVERY (the step-2 gap above — the next MP task); interactive 2-window
|
Cross-pod damage WIRE DELIVERY (the step-2 gap above — the next MP task); interactive 2-window
|
||||||
|
|||||||
@@ -241,6 +241,13 @@ void
|
|||||||
// If this is a replicant instance, reroute the message to the master
|
// If this is a replicant instance, reroute the message to the master
|
||||||
//-------------------------------------------------------------------
|
//-------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
|
if (getenv("BT_MP_NET") && message->messageID != 6)
|
||||||
|
DEBUG_STREAM << "[mp-disp] Entity::Dispatch msgID=" << (int)message->messageID
|
||||||
|
<< " inst=" << (int)GetInstance()
|
||||||
|
<< (GetInstance() == ReplicantInstance ? " REPLICANT -> reroute to master"
|
||||||
|
: (IsValid() ? " master/valid -> local Receive" : " INVALID -> Post"))
|
||||||
|
<< "\n" << std::flush;
|
||||||
|
|
||||||
if (GetInstance() == ReplicantInstance)
|
if (GetInstance() == ReplicantInstance)
|
||||||
{
|
{
|
||||||
application->SendMessage(
|
application->SendMessage(
|
||||||
|
|||||||
@@ -258,6 +258,11 @@ void
|
|||||||
Event::Process()
|
Event::Process()
|
||||||
{
|
{
|
||||||
Check(this);
|
Check(this);
|
||||||
|
// MP DIAGNOSTIC (task #47): a network-delivered TakeDamage (msgID 21) is
|
||||||
|
// carried by an event -- confirm it actually gets Processed (vs deferred).
|
||||||
|
if (messageToSend && messageToSend->messageID == 21 && getenv("BT_MP_NET"))
|
||||||
|
DEBUG_STREAM << "[mp-evt] Event::Process TakeDamage target="
|
||||||
|
<< (void*)targetReceiver.GetCurrent() << "\n" << std::flush;
|
||||||
targetReceiver.GetCurrent()->Receive(this);
|
targetReceiver.GetCurrent()->Receive(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,23 @@ void
|
|||||||
Entity
|
Entity
|
||||||
*entity =
|
*entity =
|
||||||
application->GetHostManager()->GetEntityPointer(message->entityID);
|
application->GetHostManager()->GetEntityPointer(message->entityID);
|
||||||
|
|
||||||
|
// MP DIAGNOSTIC (task #47, BT_MP_NET): log every incoming entity-manager
|
||||||
|
// message (a dispatched cross-pod message arrives here) + whether the
|
||||||
|
// target entity was found. msgID 6 (update records) filtered as noise.
|
||||||
|
if (getenv("BT_MP_NET") && message->messageID != 6)
|
||||||
|
DEBUG_STREAM << "[mp-recv] msgID=" << (int)message->messageID
|
||||||
|
<< " entityID=" << message->entityID
|
||||||
|
<< " len=" << (int)message->messageLength
|
||||||
|
<< (entity ? " ENTITY FOUND" : " NO ENTITY (remake?)")
|
||||||
|
<< (entity ? " ptr=" : "") << (entity ? (void*)entity : (void*)0)
|
||||||
|
<< (entity ? " classID=" : "") ;
|
||||||
|
if (getenv("BT_MP_NET") && message->messageID != 6 && entity)
|
||||||
|
DEBUG_STREAM << (int)entity->GetClassID()
|
||||||
|
<< ((int)entity->GetClassID() == 0xBB9 ? " (MECH) -> Post" : " (NOT MECH!) -> Post");
|
||||||
|
if (getenv("BT_MP_NET") && message->messageID != 6)
|
||||||
|
DEBUG_STREAM << "\n" << std::flush;
|
||||||
|
|
||||||
if (entity)
|
if (entity)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -98,6 +98,12 @@ void
|
|||||||
//
|
//
|
||||||
Receiver::SharedData *sharedData = GetSharedData();
|
Receiver::SharedData *sharedData = GetSharedData();
|
||||||
Handler handler = sharedData->activeMessageHandlers->Find(what->messageID);
|
Handler handler = sharedData->activeMessageHandlers->Find(what->messageID);
|
||||||
|
// MP DIAGNOSTIC (task #47): for a TakeDamage (msgID 21), report whether a
|
||||||
|
// handler is found on this receiver (the cross-pod delivery endpoint).
|
||||||
|
if (what->messageID == 21 && getenv("BT_MP_NET"))
|
||||||
|
DEBUG_STREAM << "[mp-recv2] Receive TakeDamage this=" << (void*)this
|
||||||
|
<< " handler=" << (handler != Receiver::NullHandler ? "FOUND" : "NULL")
|
||||||
|
<< "\n" << std::flush;
|
||||||
if (handler != Receiver::NullHandler)
|
if (handler != Receiver::NullHandler)
|
||||||
{
|
{
|
||||||
(this->*handler)(what);
|
(this->*handler)(what);
|
||||||
|
|||||||
@@ -1140,6 +1140,14 @@ void L4NetworkManager::Send(
|
|||||||
//Net_Common_Ptr->Buffer_Length = (short)SEND_BUFFER_SIZE(packet_size);
|
//Net_Common_Ptr->Buffer_Length = (short)SEND_BUFFER_SIZE(packet_size);
|
||||||
//send_packet_request->Socket_Ptr = l4host->GetNetworkSocket();
|
//send_packet_request->Socket_Ptr = l4host->GetNetworkSocket();
|
||||||
//NetNub::SendCommand();
|
//NetNub::SendCommand();
|
||||||
|
// MP DIAGNOSTIC (task #47, BT_MP_NET): log every non-update send so a
|
||||||
|
// dispatched cross-pod message (e.g. TakeDamage) is visible on the wire.
|
||||||
|
// (Update records are msgID 6, ~144 bytes -- filtered out to cut noise.)
|
||||||
|
if (getenv("BT_MP_NET") && !(message->messageID == 6 && message->messageLength <= 160))
|
||||||
|
DEBUG_STREAM << "[mp-send] host=" << (long)host_ID << " client=" << (int)client
|
||||||
|
<< " msgID=" << (int)message->messageID
|
||||||
|
<< " len=" << (int)message->messageLength << "\n" << std::flush;
|
||||||
|
|
||||||
send(l4host->GetNetworkSocket(), (char *)my_temp_packet, packet_size, 0);
|
send(l4host->GetNetworkSocket(), (char *)my_temp_packet, packet_size, 0);
|
||||||
free(my_temp_packet);
|
free(my_temp_packet);
|
||||||
// Check for errors and abort if there were any
|
// Check for errors and abort if there were any
|
||||||
|
|||||||
@@ -548,6 +548,17 @@ void
|
|||||||
Mech::TakeDamageMessageHandler(TakeDamageMessage *message)
|
Mech::TakeDamageMessageHandler(TakeDamageMessage *message)
|
||||||
{
|
{
|
||||||
Check(message);
|
Check(message);
|
||||||
|
|
||||||
|
// MP DIAGNOSTIC (task #47): confirm the handler runs on the OWNING MASTER
|
||||||
|
// for a cross-pod (network-delivered) TakeDamage + what state it carries.
|
||||||
|
if (getenv("BT_MP_NET"))
|
||||||
|
DEBUG_STREAM << "[mp-hdlr] TakeDamageHandler this=" << (void*)this
|
||||||
|
<< " inst=" << (int)GetInstance()
|
||||||
|
<< " invalidZone=" << (int)message->invalidDamageZone
|
||||||
|
<< " amount=" << message->damageData.damageAmount
|
||||||
|
<< " table=" << (void*)damageLookupTable
|
||||||
|
<< " zones=" << damageZoneCount << "\n" << std::flush;
|
||||||
|
|
||||||
// Maintain the last-attacker bookkeeping (mech[0x43c]): read by the
|
// Maintain the last-attacker bookkeeping (mech[0x43c]): read by the
|
||||||
// DamageZone LOD router (same-attacker redirect reuse, mechdmg.cpp:374)
|
// DamageZone LOD router (same-attacker redirect reuse, mechdmg.cpp:374)
|
||||||
// and by the damage-band effect orientation. Was declared but never
|
// and by the damage-band effect orientation. Was declared but never
|
||||||
|
|||||||
@@ -2397,9 +2397,10 @@ void
|
|||||||
if (m == 0) continue;
|
if (m == 0) continue;
|
||||||
Point3D mp = m->localOrigin.linearPosition;
|
Point3D mp = m->localOrigin.linearPosition;
|
||||||
DEBUG_STREAM << "[mp-cand] " << (void*)m
|
DEBUG_STREAM << "[mp-cand] " << (void*)m
|
||||||
|
<< " entityID=" << m->GetEntityID()
|
||||||
|
<< " ownerID=" << (int)m->GetOwnerID()
|
||||||
<< " inst=" << (int)m->GetInstance()
|
<< " inst=" << (int)m->GetInstance()
|
||||||
<< " interest=" << m->interestCount
|
<< " classID=" << (int)m->GetClassID()
|
||||||
<< " dead=" << (int)m->IsMechDestroyed()
|
|
||||||
<< " pos=(" << mp.x << "," << mp.y << "," << mp.z << ")"
|
<< " pos=(" << mp.x << "," << mp.y << "," << mp.z << ")"
|
||||||
<< " zones=" << m->damageZoneCount << "\n" << std::flush;
|
<< " zones=" << m->damageZoneCount << "\n" << std::flush;
|
||||||
}
|
}
|
||||||
@@ -2429,10 +2430,11 @@ void
|
|||||||
Entity::TakeDamageMessageID,
|
Entity::TakeDamageMessageID,
|
||||||
sizeof(Entity::TakeDamageMessage),
|
sizeof(Entity::TakeDamageMessage),
|
||||||
GetEntityID(), -1, dmg);
|
GetEntityID(), -1, dmg);
|
||||||
|
DEBUG_STREAM << "[mp-force] PRE m->GetEntityID()=" << m->GetEntityID()
|
||||||
|
<< " td.entityID=" << td.entityID << "\n" << std::flush;
|
||||||
((Entity *)m)->Dispatch(&td);
|
((Entity *)m)->Dispatch(&td);
|
||||||
DEBUG_STREAM << "[mp-force] dispatched TakeDamage at replicant "
|
DEBUG_STREAM << "[mp-force] POST td.entityID=" << td.entityID
|
||||||
<< (void*)m << " ownerID=" << (long)m->GetOwnerID()
|
<< " (== what Dispatch put on the wire)\n" << std::flush;
|
||||||
<< " (reroute -> master via SendMessage)\n" << std::flush;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user