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 cleana9c3e96baseline -- 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>
646 lines
16 KiB
C++
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
|