THE CLASS OF BUG. A default-constructed Receiver::MessageHandlerSet is a total blackhole: entryCount 0 and NO parent chain, so Find() returns NullHandler for every id and Receiver::Receive dropped the message with no log, no counter, nothing. docs/INPUT_PATH_AUDIT.md ranked this systemic cause #1; running its own lint finds SEVEN such sets (Sensor, Searchlight, ThermalSight, Gyroscope, MechTech, SubsystemMessageManager, Torso). It is also why generators cannot be switched off (#53) -- same shape. FIX 1 -- Sensor / Avionics (the big one). The streamed mappings aim 108 records across the 18 mechs at it: the MFD2 ENG1 power bank, 6 clickable buttons plus keys F5-F9 (route Avionics power to generators A-D, gen mode, coolant cut). All silently swallowed. Sensor needed NO handlers of its own -- ids 4-8 already live on PoweredSubsystem, which chains HeatSink for id 3, covering the whole 3-8 range those buttons send. It only ever needed to CHAIN, which is exactly why the byte-identical bank on ENG2 works (Myomers chains, myomers.cpp:88-100; Sensor did not). Added Sensor::GetMessageHandlers() with the same function-local-static idiom and pointed DefaultData at it instead of the empty set. VERIFIED LIVE (scratchpad/avionics.py -- pages MFD2 and presses the bank): [gensel] Avionics -> GeneratorA (tapped) ... B, C, D [gensel] Avionics mode -> 2 i.e. Avionics now behaves exactly like Myomers. 24 handler hits, 0 unhandled. FIX 2 -- the standing guard. Receiver::Receive now logs once per (class, message id) pair when no handler is found: '[msg] UNHANDLED: <class> has no handler for message id N -- silently dropped (is its MessageHandlerSet chained?)'. Once-per-pair on purpose, since some ids broadcast every frame. This one line would have printed Avionics, Searchlight, ThermalSight, crouch and all four generator buttons on the first boot instead of costing us months of player reports. Uses SharedData::derivedClasses -> Derivation::className. DEFERRED, with reasons (not guessed at): Searchlight's ToggleLamp body exists but is unreachable -- it needs a correctly-typed forwarder (Receiver::Handler is void(const Message*); ToggleLamp is Logical(Message&), so a cast would be UB that happens to work on x86) AND an id check, because its id 3 collides with HeatSink's ToggleCooling and I have not traced whether PowerWatcher's chain reaches HeatSink. #53 needs more than chaining: ToggleGeneratorOnOff @004b1ed0 has no body at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0166KTsC7ADm7VXEi1HF1jNg
724 lines
19 KiB
C++
724 lines
19 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);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// UNHANDLED-MESSAGE TRACE (input-path audit, systemic cause #1).
|
|
//
|
|
// A message with no handler was dropped here in complete silence -- no log,
|
|
// no counter -- and a DEFAULT-CONSTRUCTED `MessageHandlerSet` has
|
|
// entryCount 0 and no parent chain, so it swallows EVERY id. That is how
|
|
// the Avionics power bank (108 streamed records: 6 cockpit buttons + keys
|
|
// F5-F9), the searchlight toggle, the thermal sight, the crouch button and
|
|
// all four generator ON/OFF buttons were dead for months while looking
|
|
// perfectly wired. This trace is the standing guard for the whole class:
|
|
// one line per (class, message id) pair, first occurrence only, so a newly
|
|
// dead control announces itself on the first boot instead of waiting for a
|
|
// player report.
|
|
//
|
|
// Deliberately once-per-pair: some ids are broadcast every frame, and this
|
|
// must never become a per-frame log.
|
|
//
|
|
static const int kMaxSeen = 64;
|
|
static struct { const char *name; int id; } seen[kMaxSeen];
|
|
static int seenCount = 0;
|
|
|
|
Derivation *derivation = sharedData->derivedClasses;
|
|
const char *class_name = (derivation != 0 && derivation->className != 0)
|
|
? derivation->className : "<unnamed receiver>";
|
|
const int id = (int)what->messageID;
|
|
|
|
int known = 0;
|
|
for (int i = 0; i < seenCount; ++i)
|
|
{
|
|
if (seen[i].id == id && seen[i].name == class_name)
|
|
{
|
|
known = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (!known && seenCount < kMaxSeen)
|
|
{
|
|
seen[seenCount].name = class_name;
|
|
seen[seenCount].id = id;
|
|
++seenCount;
|
|
DEBUG_STREAM << "[msg] UNHANDLED: " << class_name
|
|
<< " has no handler for message id " << id
|
|
<< " -- silently dropped (is its MessageHandlerSet chained?)"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
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);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// FAITHFUL FIX (Gitea #18 / reconstruction-gotchas #11 -- the glass
|
|
// dead-button crash). Find(id) returns messageHandlers[id-1].entryHandler
|
|
// for ANY id <= entryCount with no populated-check (RECEIVER.h Find), and
|
|
// Receive() calls it whenever it is != NullHandler. A message id that NO
|
|
// class in the receiver's chain registers but that is < entryCount lands on
|
|
// a GAP slot -- e.g. the streamed Eng-page .CTL dispatches subsystem msg id
|
|
// 3 / 0xb to a shown weapon (Emitter) whose handler chain only registers
|
|
// PoweredSubsystem 4-8 + MechWeapon 9-10, so slot[2] (id 3) is a gap below
|
|
// entryCount 10. operator new[] leaves these POD slots UNINITIALISED, so
|
|
// the gap held heap garbage and Receive did (this->*garbage)(msg) -> AV
|
|
// (debug CRT fill 0xCDCDCDCD; a recycled release block 0x01048748). The
|
|
// 1995 binary has the IDENTICAL non-zeroing new[] (part_002.c Build) and
|
|
// only survived on fresh-OS-heap-zero luck (a zero slot == NullHandler ==
|
|
// ignored). Make that intended "Receiver ignores unhandled messages"
|
|
// DETERMINISTIC: null every slot up front so a gap is NullHandler (Receive
|
|
// drops it) with an empty -- never NULL -- name so the name-based Find()
|
|
// strcmp cannot dereference a gap. Inherited/supplied handlers overwrite
|
|
// their slots below; only the true gaps stay null.
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
for (i=0; i<entryCount; ++i)
|
|
{
|
|
messageHandlers[i].entryID = 0;
|
|
messageHandlers[i].entryName = "";
|
|
messageHandlers[i].entryHandler = Receiver::NullHandler;
|
|
}
|
|
|
|
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
|