Files
BT411/engine/MUNGA/RECEIVER.cpp
arcattackandClaude Fable 5 04a2049ce2 MP task #47: DISPROVE the Entity-layout root cause; correct the record
The prior commit (c78662a) filed cross-pod damage failure under a
"cross-TU Entity/EntityID layout divergence" (and the P5 base-region
audit). A compile-time offsetof probe injected into BOTH translation
units disproves that: game/reconstructed AND engine/MUNGA compute an
IDENTICAL Entity layout --

  sizeof(Entity)=444  offsetof(entityID)=380 (0x17C)
  offsetof(ownerID)=388  offsetof(simulationFlags)=32
  offsetof(Mech,entityID)=380  (Entity subobject at 0)

So Entity::Dispatch reads this->entityID at the SAME offset mech4's
GetEntityID() does -- there is no per-TU read difference, and the
logged 3:22 (mech4 candidate) vs 3:19 (engine Dispatch) cannot be one
object read two ways. Those lines were different objects/messages,
mis-correlated. The distinct, REAL P5 "base-region layout divergence"
(HARD_PROBLEMS.md) is about raw-offset stomps at this+0x2d4..0x2f0 --
far above entityID -- and is unrelated to #47.

Changes:
- Revert the committed BT_MP_NET engine diagnostics (ENTITY/EVENT/
  NTTMGR/RECEIVER/L4NET) to the clean a9c3e96 baseline -- those are
  the very instruments that produced the mis-correlated data.
- mech4 BT_MP_FORCE_DMG hook: dispatch via the real m->Dispatch(&td)
  path (no false id stamping); comment records the offsetof finding.
- context/multiplayer.md: layout-divergence RULED OUT; 3:22/3:19 marked
  unconfirmed; leading hypotheses reframed as H2 (wire host-relative
  (de)serialization) vs H3 (replicant id != master's registered key),
  to be distinguished by a 2-node run with per-message correlation.

Solo un-regressed (mech walks + targets, 0 faults). Cross-pod delivery
remains open, but the investigation is redirected off the wrong (large,
structural) layout-audit path onto the EntityID wire/id-assignment path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:09:50 -05:00

646 lines
16 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "receiver.h"
#include "event.h"
//#############################################################################
//############################# Receiver ################################
//#############################################################################
const Receiver::Handler
Receiver::NullHandler=NULL;
Receiver::SharedData
Receiver::DefaultData(
Receiver::GetClassDerivations(),
Receiver::GetMessageHandlers()
);
Derivation* Receiver::GetClassDerivations()
{
static Derivation classDerivations("Receiver");
return &classDerivations;
}
const Receiver::HandlerEntry
Receiver::MessageHandlerEntries[]=
{
{
Receiver::WatcherChangedMessageID,
"WatcherChanged",
(Receiver::Handler)&Receiver::DefaultMessageHandler
},
{
Receiver::LoadResourceFinishedMessageID,
"LoadResourceFinished",
(Receiver::Handler)&Receiver::DefaultMessageHandler
}
};
Receiver::MessageHandlerSet& Receiver::GetMessageHandlers()
{
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(Receiver::MessageHandlerEntries), Receiver::MessageHandlerEntries);
return messageHandlers;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if defined(USE_SIGNATURE)
int
Is_Signature_Bad(const volatile Receiver__Message *)
{
return False;
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
#if DEBUG_LEVEL>0
Receiver::DefaultMessageHandler(Message* message)
#else
Receiver::DefaultMessageHandler(Message*)
#endif
{
Tell(
"Default Handler activated on message ID " << message->messageID << endl
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Receiver::Receive(Message *what)
{
//
//-------------------------------------------------------------------------
// Service the message taps by creating an iterator to step through all the
// MessageTaps registered with the Derivation object for the receiver's
// class. Each tap will do whatever it thinks best about this receiver
// having received this message
//-------------------------------------------------------------------------
//
Check(this);
Check_Pointer(what);
ChainIteratorOf<MessageTap*> i(GetDerivation()->activeTaps);
MessageTap *tap;
while ((tap = i.ReadAndNext()) != NULL)
{
tap->Scan(this,what);
}
//
//--------------------------------------------------------------------------
// Now, see if the receiver can handle this type of message, and if so, send
// the message to the returned method handler
//--------------------------------------------------------------------------
//
Receiver::SharedData *sharedData = GetSharedData();
Handler handler = sharedData->activeMessageHandlers->Find(what->messageID);
if (handler != Receiver::NullHandler)
{
(this->*handler)(what);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Receiver::Receive(Event *event)
{
Receive(event->messageToSend);
Unregister_Object(event);
delete event;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Receiver::FlushEvents(int) // max_priority doesn't work yet!!!!
{
Check(this);
PlugIteratorOf<Event*> i(this);
Event *event;
while ((event = i.ReadAndNext()) != NULL)
{
if (event->GetClassID() == EventClassID)
{
Unregister_Object(event);
delete event;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Receiver::FlushMatchingEvents(
Receiver::MessageID target_message,
int // max_priority doesn't work yet!!!!
)
{
Check(this);
PlugIteratorOf<Event*>
i(this);
Event* event;
while ((event = i.ReadAndNext()) != NULL)
{
if (
event->GetClassID() == EventClassID && (
target_message == Receiver::AnyMessageID
|| event->messageToSend->messageID == target_message
)
)
{
Unregister_Object(event);
delete event;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Receiver::ProcessDeferredQueue()
{
//
//------------------------------------------------
// Find a delay queue, and return if there is none
//------------------------------------------------
//
Check(this);
PlugIteratorOf<DeferredEventQueue*> i(this);
DeferredEventQueue *deferred_queue;
while ((deferred_queue = i.ReadAndNext()) != NULL)
{
if (deferred_queue->GetClassID() == DeferredEventQueueClassID)
{
break;
}
}
if (!deferred_queue)
{
return;
}
//
//--------------------------------------------------------------
// Now, tell the queue to process all its events, then delete it
//--------------------------------------------------------------
//
deferred_queue->ProcessAllEvents();
Unregister_Object(deferred_queue);
delete deferred_queue;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Receiver::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}
//#############################################################################
//######################### Receiver::Message ###########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Receiver::Message::TestInstance() const
{
return True;
}
//#############################################################################
//######################### Receiver::DynamicMessage ##########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Receiver__DynamicMessage::Receiver__DynamicMessage(
Receiver::MessageID message_ID,
size_t message_size
)
{
Receiver::Message new_message(message_ID, message_size);
WriteBytes(&new_message, message_size);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MemoryStream&
Receiver::DynamicMessage::WriteBytes(
const void *ptr,
size_t number_of_bytes
)
{
DynamicMemoryStream::WriteBytes(ptr, number_of_bytes);
Receiver::Message *message = GetMessagePointer();
size_t new_size = GetBytesUsed();
message->messageLength = new_size;
return *this;
}
//#############################################################################
//############################ MessageTap ###############################
//#############################################################################
MemoryBlock *MessageTap::GetAllocatedMemory()
{
static MemoryBlock allocatedMemory(sizeof(MessageTap), 30, 30, "Message Taps");
return &allocatedMemory;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MessageTap::MessageTap(
Derivation *derivation,
Receiver *client,
Receiver::ScanCallback call_back
):
Plug(MessageTapClassID)
{
//
//---------------------------
// Initialize the message tap
//---------------------------
//
Check(client);
clientReceiver = client;
scanCallback = call_back;
matchingReceiver = NULL;
matchingMessageID = Receiver::AnyMessageID;
//
//--------------------------------------------
// Hook the tap up to the specified derivation
//--------------------------------------------
//
Check(derivation);
derivation->AppendMessageTap(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MessageTap::MessageTap(
Derivation *derivation,
Receiver *client,
Receiver::ScanCallback call_back,
Receiver::MessageID messageID,
Receiver *receiver
):
Plug(MessageTapClassID)
{
//
//---------------------------
// Initialize the message tap
//---------------------------
//
Check(client);
clientReceiver = client;
scanCallback = call_back;
matchingReceiver = receiver;
matchingMessageID = messageID;
//
//--------------------------------------------
// Hook the tap up to the specified derivation
//--------------------------------------------
//
Check(derivation);
derivation->AppendMessageTap(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MessageTap::Scan(
Receiver *receiver,
Receiver::Message *message
)
{
Check(this);
Check(message);
if (
(!matchingReceiver || matchingReceiver == receiver)
&& (
matchingMessageID == Receiver::AnyMessageID
|| matchingMessageID == message->messageID
)
)
{
(clientReceiver->*scanCallback)(message,receiver);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
MessageTap::TestInstance() const
{
return True;
}
//#############################################################################
//##################### Receiver::InheritanceSet ########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Receiver::InheritanceSet::TestInstance() const
{
return True;
}
//#############################################################################
//#################### Receiver::MessageHandlerSet ######################
//#############################################################################
const Receiver::MessageHandlerSet
Receiver::MessageHandlerSet::NullSet;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Receiver__MessageHandlerSet::~Receiver__MessageHandlerSet()
{
if (messageHandlers)
{
Unregister_Pointer(messageHandlers);
delete[] messageHandlers;
messageHandlers = NULL;
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Receiver::MessageHandlerSet::Build(
Receiver::MessageID count,
const Receiver::HandlerEntry handler_table[],
const Receiver::MessageHandlerSet *inheritance
)
{
//
//-------------------------------------------------------
// Find out the highest message type we have to deal with
//-------------------------------------------------------
//
Check(this);
Check_Pointer(handler_table);
entryCount = 0;
Receiver::MessageID i;
for (i=0; i<count; ++i)
{
if (handler_table[i].entryID > entryCount)
{
entryCount = handler_table[i].entryID;
}
}
if (inheritance)
{
Check(inheritance);
if (entryCount < inheritance->entryCount)
{
entryCount = inheritance->entryCount;
}
#if DEBUG_LEVEL>0
else if (entryCount > inheritance->entryCount)
{
i = inheritance->entryCount+1;
goto Check_Table;
}
#endif
}
else
{
Verify(entryCount == count);
#if DEBUG_LEVEL>0
i = 1;
Check_Table:
while (i <= entryCount)
{
int j;
for (j=0; j<count; ++j)
{
if (handler_table[j].entryID == i)
{
break;
}
}
if (j == count)
{
break;
}
++i;
}
Verify(i > count);
#endif
}
//
//-----------------------------------------------------------------------
// Allocate the memory for the new handler set, and copy the inherited
// handlers to the new table. We are guaranteed to have enough space for
// the inherited table
//-----------------------------------------------------------------------
//
messageHandlers = new Receiver::HandlerEntry[entryCount];
Check_Pointer(messageHandlers);
Register_Pointer(messageHandlers);
i = 0;
if (inheritance)
{
for (; i<inheritance->entryCount; ++i)
{
messageHandlers[i] = inheritance->messageHandlers[i];
}
}
//
//----------------------------------------------------------------------
// Step through the new table supplied, placing each handler in the slot
// determined by the message type
//----------------------------------------------------------------------
//
for (i=0; i<count; ++i)
{
Receiver::MessageID index = handler_table[i].entryID - 1;
Verify(index >= 0 && index < entryCount);
messageHandlers[index] = handler_table[i];
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const Receiver::HandlerEntry*
Receiver::MessageHandlerSet::Find(const char* name) const
{
Check(this);
Check_Pointer(name);
for (int i=0; i<entryCount; ++i)
{
if (!strcmp(name, messageHandlers[i].entryName))
{
return &messageHandlers[i];
}
}
return NULL;
}
//#############################################################################
//############################# Derivation ##############################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Derivation::Derivation(char *name):
Node(DerivationClassID),
classDerivations(this),
activeTaps(this)
{
Dump(name);
className = name;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Derivation::Derivation(
Derivation *derivation,
char *name
):
Node(DerivationClassID),
classDerivations(this),
activeTaps(this)
{
//
//----------------------------------------------------------
// Add this derivation as a child of the parent's derivation
//----------------------------------------------------------
//
Dump(name);
className = name;
Check(derivation);
if (derivation)
derivation->AppendDerivation(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Derivation::AppendMessageTap(MessageTap* tap)
{
Check(this);
Check(tap);
//
//----------------------------------------------------------------------
// Add the tap to the taps chain, then step through all our derivations,
// sending each of them this same method
//----------------------------------------------------------------------
//
activeTaps.Add(tap);
ChainIteratorOf<Derivation*>
i(classDerivations);
Derivation *derivation;
while ((derivation = i.ReadAndNext()) != NULL)
{
derivation->AppendMessageTap(tap);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Derivation::IsDescendedFrom(Derivation& parent)
{
Check(this);
Check(&parent);
//
//------------------------------
// We are descended from ourself
//------------------------------
//
if (&parent == this)
{
return True;
}
//
//----------------------------------------------------------------------
// Find our parent derivation, and ask him
// sending each of them this same method
//----------------------------------------------------------------------
//
PlugIteratorOf<Derivation*> i(this);
Derivation *derivation;
while ((derivation = i.ReadAndNext()) != NULL)
{
if (derivation->GetClassID() == DerivationClassID)
{
return derivation->IsDescendedFrom(parent);
}
}
return False;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
Derivation::IsDescendedFrom(const char* parent)
{
Check(this);
Check_Pointer(parent);
//
//------------------------------
// We are descended from ourself
//------------------------------
//
Check_Pointer(className);
if (!strcmp(className, parent))
{
return True;
}
//
//----------------------------------------------------------------------
// Find our parent derivation, and ask him
// sending each of them this same method
//----------------------------------------------------------------------
//
PlugIteratorOf<Derivation*> i(this);
Derivation *derivation;
while ((derivation = i.ReadAndNext()) != NULL)
{
Check(derivation);
if (derivation->GetClassID() == DerivationClassID)
{
return derivation->IsDescendedFrom(parent);
}
}
return False;
}
//#############################################################################
//###################### Receiver::SharedData ##########################
//#############################################################################
Logical
Receiver::SharedData::TestInstance() const
{
return True;
}
#if defined(TEST_CLASS)
# include "receiver.tcp"
#endif