Files
BT411/engine/MUNGA/NTTMGR.cpp
T
arcattackandClaude Fable 5 c78662a0f1 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>
2026-07-09 11:03:32 -05:00

234 lines
6.3 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "nttmgr.h"
#include "namelist.h"
#include "app.h"
#include "entity.h"
#include "hostmgr.h"
#include "dropzone.h"
#include "registry.h"
//#############################################################################
//############################ EntityGroup ##############################
//#############################################################################
//#############################################################################
// Construction and Destruction
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityGroup::EntityGroup():
groupMembers(NULL)
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityGroup::~EntityGroup()
{
}
//#############################################################################
//########################### EntityManager #############################
//#############################################################################
Derivation* EntityManager::GetClassDerivations()
{
static Derivation classDerivations(NetworkClient::GetClassDerivations(), "EntityManager");
return &classDerivations;
}
EntityManager::SharedData
EntityManager::DefaultData(
EntityManager::GetClassDerivations(),
EntityManager::GetMessageHandlers()
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityManager::EntityManager():
NetworkClient(
EntityManager::EntityManagerClassID,
DefaultData,
NetworkClient::EntityManagerClientID
)
{
deathRow = UseGroup("DeathRow");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityManager::~EntityManager()
{
//
//----------------------------------------------
// Erase all the groups listed in the group list
//----------------------------------------------
//
ObjectNameList::Entry *entry;
while ((entry = groupList.GetFirstEntry()) != NULL)
{
Check(entry);
EntityGroup *group = (EntityGroup*)entry->GetData();
groupList.DeleteEntry(entry->GetName());
Unregister_Object(group);
delete group;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
EntityManager::FryDeathRow()
{
Check(this);
Check(deathRow);
//
// ECH 8/23/95 - Just delete one ... this allows the application to
// delete as many entities as it can within the current frame
//
ChainIteratorOf<Node*> iterator(deathRow->groupMembers);
Node *node;
if ((node = iterator.GetCurrent()) != NULL)
{
Unregister_Object(node);
delete node;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
EntityManager::ReceiveNetworkPacket(
NetworkPacket*,
Receiver::Message *packet_message
)
{
Check(this);
Check_Pointer(packet_message);
//
//-------------------------------------------------------------------------
// Check to see if the interest zone contained in the message is one we are
// interested in, and if not, ignore the message
//-------------------------------------------------------------------------
//
Entity::MakeMessage
*message = Cast_Object(Entity::MakeMessage*, packet_message);
//
//-----------------------------------------------------------------------
// Find the address of the entity. If it exists, post the message on the
// application event queue.
//-----------------------------------------------------------------------
//
Entity
*entity =
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)
{
//
// HACK -- This hack puts dropzone messages on max priority. It should
// really be done with a message priority encoded in the message
//
if (
entity->GetClassID() == DropZoneClassID
&& message->messageID == DropZone::AssignDropZoneMessageID
)
{
application->Post(MaxEventPriority, entity, message);
}
else
{
application->Post(EntityManagerEventPriority, entity, message);
}
}
//
//--------------------------------------------------------------------------
// If the entity does not exist, check to see if this is a manager message,
// or see if we have to ask for a remake
//--------------------------------------------------------------------------
//
else
{
switch (message->messageID)
{
case Entity::MakeMessageID:
{
#if DEBUG_LEVEL>0
Entity *entity = application->GetRegistry()->MakeEntity(message);
Register_Object(entity);
#else
application->GetRegistry()->MakeEntity(message);
#endif
}
break;
//
//---------------------------------------------------------------------
// Send a remake message to the sending host to have our entity rebuilt
//---------------------------------------------------------------------
//
default:
break;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
EntityGroup*
EntityManager::UseGroup(const char *name)
{
EntityGroup *group = FindGroup(name);
if (!group)
{
group = new EntityGroup;
group->groupName = groupList.AddEntry(name, group);
Register_Object(group);
}
return group;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
EntityManager::RemoveGroup(EntityGroup *group)
{
Check(group);
groupList.DeleteEntry(group->groupName);
Unregister_Object(group);
delete group;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
EntityManager::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}