Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,421 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "util/messaging/dispatcher.h"
#include "platform/threads/mutex.h"
#include "core/tSimpleHashTable.h"
#include "core/util/safeDelete.h"
namespace Dispatcher
{
//-----------------------------------------------------------------------------
// IMessageListener Methods
//-----------------------------------------------------------------------------
IMessageListener::~IMessageListener()
{
for(S32 i = 0;i < mQueues.size();i++)
{
unregisterMessageListener(mQueues[i], this);
}
}
void IMessageListener::onAddToQueue(StringTableEntry queue)
{
// [tom, 8/20/2006] The dispatcher won't let us get added twice, so no need
// to worry about it here.
mQueues.push_back(queue);
}
void IMessageListener::onRemoveFromQueue(StringTableEntry queue)
{
for(S32 i = 0;i < mQueues.size();i++)
{
if(mQueues[i] == queue)
{
mQueues.erase(i);
return;
}
}
}
//-----------------------------------------------------------------------------
// Global State
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @brief Internal class used by the dispatcher
//-----------------------------------------------------------------------------
typedef struct _DispatchData
{
void *mMutex;
SimpleHashTable<MessageQueue> mQueues;
U32 mLastAnonQueueID;
_DispatchData()
{
mMutex = Mutex::createMutex();
mLastAnonQueueID = 0;
}
~_DispatchData()
{
if(Mutex::lockMutex( mMutex ) )
{
mQueues.clearTables();
Mutex::unlockMutex( mMutex );
}
Mutex::destroyMutex( mMutex );
//SAFE_DELETE(mMutex);
mMutex = NULL;
}
const char *makeAnonQueueName()
{
char buf[512];
dSprintf(buf, sizeof(buf), "AnonQueue.%lu", mLastAnonQueueID++);
return StringTable->insert(buf);
}
} _DispatchData;
static _DispatchData& _dispatcherGetGDispatchData()
{
static _DispatchData dispatchData;
return dispatchData;
}
#define gDispatchData _dispatcherGetGDispatchData()
//-----------------------------------------------------------------------------
// Queue Registration
//-----------------------------------------------------------------------------
bool isQueueRegistered(const char *name)
{
MutexHandle mh;
if(mh.lock(gDispatchData.mMutex, true))
{
return gDispatchData.mQueues.retreive(name) != NULL;
}
return false;
}
void registerMessageQueue(const char *name)
{
if(isQueueRegistered(name))
return;
if(Mutex::lockMutex( gDispatchData.mMutex, true ))
{
MessageQueue *queue = new MessageQueue;
queue->mQueueName = StringTable->insert(name);
gDispatchData.mQueues.insert(queue, name);
Mutex::unlockMutex( gDispatchData.mMutex );
}
}
extern const char * registerAnonMessageQueue()
{
const char *name = NULL;
if(Mutex::lockMutex( gDispatchData.mMutex, true ))
{
name = gDispatchData.makeAnonQueueName();
Mutex::unlockMutex( gDispatchData.mMutex );
}
if(name)
registerMessageQueue(name);
return name;
}
void unregisterMessageQueue(const char *name)
{
MutexHandle mh;
if(mh.lock(gDispatchData.mMutex, true))
{
MessageQueue *queue = gDispatchData.mQueues.remove(name);
if(queue == NULL)
return;
// Tell the listeners about it
for(S32 i = 0;i < queue->mListeners.size();i++)
{
queue->mListeners[i]->onRemoveFromQueue(name);
}
delete queue;
}
}
//-----------------------------------------------------------------------------
// Message Listener Registration
//-----------------------------------------------------------------------------
bool registerMessageListener(const char *queue, IMessageListener *listener)
{
if(! isQueueRegistered(queue))
registerMessageQueue(queue);
MutexHandle mh;
if(! mh.lock(gDispatchData.mMutex, true))
return false;
MessageQueue *q = gDispatchData.mQueues.retreive(queue);
if(q == NULL)
{
Con::errorf("Dispatcher::registerMessageListener - Queue '%s' not found?! It should have been added automatically!", queue);
return false;
}
for(VectorPtr<IMessageListener *>::iterator i = q->mListeners.begin();i != q->mListeners.end();i++)
{
if(*i == listener)
return false;
}
q->mListeners.push_front(listener);
listener->onAddToQueue(StringTable->insert(queue));
return true;
}
void unregisterMessageListener(const char *queue, IMessageListener *listener)
{
if(! isQueueRegistered(queue))
return;
MutexHandle mh;
if(! mh.lock(gDispatchData.mMutex, true))
return;
MessageQueue *q = gDispatchData.mQueues.retreive(queue);
if(q == NULL)
return;
for(VectorPtr<IMessageListener *>::iterator i = q->mListeners.begin();i != q->mListeners.end();i++)
{
if(*i == listener)
{
listener->onRemoveFromQueue(StringTable->insert(queue));
q->mListeners.erase(i);
return;
}
}
}
//-----------------------------------------------------------------------------
// Dispatcher
//-----------------------------------------------------------------------------
bool dispatchMessage( const char* queue, const char* msg, const char* data)
{
AssertFatal( queue != NULL, "Dispatcher::dispatchMessage - Got a NULL queue name" );
AssertFatal( msg != NULL, "Dispatcher::dispatchMessage - Got a NULL message" );
MutexHandle mh;
if(! mh.lock(gDispatchData.mMutex, true))
return true;
MessageQueue *q = gDispatchData.mQueues.retreive(queue);
if(q == NULL)
{
Con::errorf("Dispatcher::dispatchMessage - Attempting to dispatch to unknown queue '%s'", queue);
return true;
}
return q->dispatchMessage(msg, data);
}
bool dispatchMessageObject(const char *queue, Message *msg)
{
MutexHandle mh;
if(msg == NULL)
return true;
msg->addReference();
if(! mh.lock(gDispatchData.mMutex, true))
{
msg->freeReference();
return true;
}
MessageQueue *q = gDispatchData.mQueues.retreive(queue);
if(q == NULL)
{
Con::errorf("Dispatcher::dispatchMessage - Attempting to dispatch to unknown queue '%s'", queue);
msg->freeReference();
return true;
}
// [tom, 8/19/2006] Make sure that the message is registered with the sim, since
// when it's ref count is zero it'll be deleted with deleteObject()
if(! msg->isProperlyAdded())
{
SimObjectId id = Message::getNextMessageID();
if(id != 0xffffffff)
msg->registerObject(id);
else
{
Con::errorf("dispatchMessageObject: Message was not registered and no more object IDs are available for messages");
msg->freeReference();
return false;
}
}
bool bResult = q->dispatchMessageObject(msg);
msg->freeReference();
return bResult;
}
//-----------------------------------------------------------------------------
// Internal Functions
//-----------------------------------------------------------------------------
MessageQueue * getMessageQueue(const char *name)
{
return gDispatchData.mQueues.retreive(name);
}
extern bool lockDispatcherMutex()
{
return Mutex::lockMutex(gDispatchData.mMutex);
}
extern void unlockDispatcherMutex()
{
Mutex::unlockMutex(gDispatchData.mMutex);
}
} // end namespace Dispatcher
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
using namespace Dispatcher;
ConsoleFunction(isQueueRegistered, bool, 2, 2, "(string queueName)"
"@brief Determines if a dispatcher queue exists\n\n"
"@param queueName String containing the name of queue\n"
"@ingroup Messaging")
{
return isQueueRegistered(argv[1]);
}
ConsoleFunction(registerMessageQueue, void, 2, 2, "(string queueName)"
"@brief Registeres a dispatcher queue\n\n"
"@param queueName String containing the name of queue\n"
"@ingroup Messaging")
{
return registerMessageQueue(argv[1]);
}
ConsoleFunction(unregisterMessageQueue, void, 2, 2, "(string queueName)"
"@brief Unregisters a dispatcher queue\n\n"
"@param queueName String containing the name of queue\n"
"@ingroup Messaging")
{
return unregisterMessageQueue(argv[1]);
}
//-----------------------------------------------------------------------------
ConsoleFunction(registerMessageListener, bool, 3, 3, "(string queueName, string listener)"
"@brief Registers an event message\n\n"
"@param queueName String containing the name of queue to attach listener to\n"
"@param listener Name of event messenger\n"
"@ingroup Messaging")
{
IMessageListener *listener = dynamic_cast<IMessageListener *>(Sim::findObject(argv[2]));
if(listener == NULL)
{
Con::errorf("registerMessageListener - Unable to find listener object, not an IMessageListener ?!");
return false;
}
return registerMessageListener(argv[1], listener);
}
ConsoleFunction(unregisterMessageListener, void, 3, 3, "(string queueName, string listener)"
"@brief Unregisters an event message\n\n"
"@param queueName String containing the name of queue\n"
"@param listener Name of event messenger\n"
"@ingroup Messaging")
{
IMessageListener *listener = dynamic_cast<IMessageListener *>(Sim::findObject(argv[2]));
if(listener == NULL)
{
Con::errorf("unregisterMessageListener - Unable to find listener object, not an IMessageListener ?!");
return;
}
unregisterMessageListener(argv[1], listener);
}
//-----------------------------------------------------------------------------
ConsoleFunction(dispatchMessage, bool, 3, 4, "(string queueName, string message, string data)"
"@brief Dispatch a message to a queue\n\n"
"@param queueName Queue to dispatch the message to\n"
"@param message Message to dispatch\n"
"@param data Data for message\n"
"@return True for success, false for failure\n"
"@see dispatchMessageObject\n"
"@ingroup Messaging")
{
return dispatchMessage(argv[1], argv[2], argc > 3 ? argv[3] : "" );
}
ConsoleFunction(dispatchMessageObject, bool, 3, 3, "(string queueName, string message)"
"@brief Dispatch a message object to a queue\n\n"
"@param queueName Queue to dispatch the message to\n"
"@param message Message to dispatch\n"
"@return true for success, false for failure\n"
"@see dispatchMessage\n"
"@ingroup Messaging")
{
Message *msg = dynamic_cast<Message *>(Sim::findObject(argv[2]));
if(msg == NULL)
{
Con::errorf("dispatchMessageObject - Unable to find message object");
return false;
}
return dispatchMessageObject(argv[1], msg);
}

View file

@ -0,0 +1,276 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _DISPATCHER_H_
#define _DISPATCHER_H_
#ifndef _MESSAGE_H_
#include "util/messaging/message.h"
#endif
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
/// @addtogroup msgsys Message System
// @{
//-----------------------------------------------------------------------------
/// @brief Namespace for the message dispatcher functions
//-----------------------------------------------------------------------------
namespace Dispatcher
{
// [tom, 2/19/2007] This semi colon prevents VS from auto indenting the comments
// below, which is really annoying when you're trying to write docs.
;
/// @addtogroup msgsys Message System
// @{
//-----------------------------------------------------------------------------
// Interface for objects that receive messages
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @brief Listener interface for objects that receive messages
///
/// @see ScriptMsgListener
//-----------------------------------------------------------------------------
class IMessageListener
{
protected:
/// List of queues this listener is registered with.
Vector<StringTableEntry> mQueues;
public:
virtual ~IMessageListener();
//-----------------------------------------------------------------------------
/// @brief Callback for when messages are received
///
/// @param queue The name of the queue the message was dispatched to
/// @param msg The type of message
/// @param data The data for the message
/// @return false to prevent other listeners receiving this message, true otherwise
/// @see onMessageObjectReceived()
//-----------------------------------------------------------------------------
virtual bool onMessageReceived(StringTableEntry queue, const char *msg, const char *data) = 0;
//-----------------------------------------------------------------------------
/// @brief Callback for when message objects are received
///
/// @param queue The name of the queue the message was dispatched to
/// @param msg The message object
/// @return false to prevent other listeners receiving this message, true otherwise
/// @see onMessageReceived()
//-----------------------------------------------------------------------------
virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg ) = 0;
//-----------------------------------------------------------------------------
/// @brief Callback for when the listener is added to a queue
///
/// The default implementation of onAddToQueue() and onRemoveFromQueue()
/// provide tracking of the queues this listener is added to through the
/// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue()
/// should ensure they call the parent implementation in any overrides.
///
/// @param queue The name of the queue that the listener added to
/// @see onRemoveFromQueue()
//-----------------------------------------------------------------------------
virtual void onAddToQueue(StringTableEntry queue);
//-----------------------------------------------------------------------------
/// @brief Callback for when the listener is removed from a queue
///
/// The default implementation of onAddToQueue() and onRemoveFromQueue()
/// provide tracking of the queues this listener is added to through the
/// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue()
/// should ensure they call the parent implementation in any overrides.
///
/// @param queue The name of the queue the listener was removed from
/// @see onAddToQueue()
//-----------------------------------------------------------------------------
virtual void onRemoveFromQueue(StringTableEntry queue);
};
//-----------------------------------------------------------------------------
/// @brief Internal class for tracking message queues
//-----------------------------------------------------------------------------
struct MessageQueue
{
StringTableEntry mQueueName;
VectorPtr<IMessageListener *> mListeners;
MessageQueue() : mQueueName("")
{
}
bool isEmpty() { return mListeners.size() == 0; }
bool dispatchMessage(const char* event, const char* data)
{
for(VectorPtr<IMessageListener *>::iterator i = mListeners.begin();i != mListeners.end();i++)
{
if( !(*i)->onMessageReceived(mQueueName, event, data) )
return false;
}
return true;
}
bool dispatchMessageObject(Message *msg)
{
for(VectorPtr<IMessageListener *>::iterator i = mListeners.begin();i != mListeners.end();i++)
{
if( !(*i)->onMessageObjectReceived(mQueueName, msg) )
return false;
}
return true;
}
};
//-----------------------------------------------------------------------------
// Message Dispatcher Functions
//-----------------------------------------------------------------------------
/// @name Message Queue Management
// @{
//-----------------------------------------------------------------------------
/// @brief Check if a message queue is registered
///
/// @param name The name of the message queue
/// @return true if the queue is registered, false otherwise
/// @see registerMessageQueue(), unregisterMessageQueue()
//-----------------------------------------------------------------------------
extern bool isQueueRegistered(const char *name);
//-----------------------------------------------------------------------------
/// @brief Register a message queue
///
/// @param name The name of the message queue to register
/// @see isQueueRegistered(), unregisterMessageQueue()
//-----------------------------------------------------------------------------
extern void registerMessageQueue(const char *name);
//-----------------------------------------------------------------------------
/// @brief Register an anonymous message queue
///
/// @return name of anonymous message queue for passing to other functions
/// @see isQueueRegistered(), unregisterMessageQueue()
//-----------------------------------------------------------------------------
extern const char *registerAnonMessageQueue();
//-----------------------------------------------------------------------------
/// @brief Unregister a message queue
///
/// @param name The name of the message queue
/// @see registerMessageQueue(), isQueueRegistered()
//-----------------------------------------------------------------------------
extern void unregisterMessageQueue(const char *name);
//-----------------------------------------------------------------------------
/// @brief Register a listener with a queue to receive messages
///
/// @param queue The name of the queue to register the listener with
/// @param listener The listener interface that receives messages
/// @return true for success, false otherwise
/// @see unregisterMessageListener()
//-----------------------------------------------------------------------------
extern bool registerMessageListener(const char *queue, IMessageListener *listener);
//-----------------------------------------------------------------------------
/// @brief Unregister a listener with a queue
///
/// @param queue The name of the queue to unregister the listener
/// @param listener The listener interface that was passed to registerMessageListener()
/// @see registerMessageListener()
//-----------------------------------------------------------------------------
extern void unregisterMessageListener(const char *queue, IMessageListener *listener);
// @}
/// @name Message Dispatcher
// @{
//-----------------------------------------------------------------------------
/// @brief Dispatch a message to a queue
///
/// @param queue Queue to dispatch the message to
/// @param msg Message to dispatch
/// @param data Data for message
/// @return true for success, false for failure
/// @see dispatchMessageObject()
//-----------------------------------------------------------------------------
extern bool dispatchMessage(const char *queue, const char *msg, const char *data);
//-----------------------------------------------------------------------------
/// @brief Dispatch a message object to a queue
///
/// @param queue Queue to dispatch the message to
/// @param msg Message to dispatch
/// @return true for success, false for failure
/// @see dispatchMessage()
//-----------------------------------------------------------------------------
extern bool dispatchMessageObject(const char *queue, Message *msg);
// @}
//-----------------------------------------------------------------------------
// Internal Functions
//-----------------------------------------------------------------------------
/// @name Internal Functions
// @{
//-----------------------------------------------------------------------------
/// @brief Internal function: Lock the dispatcher mutex.
/// @return true for success, false for failure
/// @see unlockDispatcherMutex()
//-----------------------------------------------------------------------------
extern bool lockDispatcherMutex();
//-----------------------------------------------------------------------------
/// @brief Internal function: Unlock the dispatcher mutex.
/// @see lockDispatcherMutex()
//-----------------------------------------------------------------------------
extern void unlockDispatcherMutex();
//-----------------------------------------------------------------------------
/// @brief Internal function: obtain message queue. Dispatcher mutex must be locked.
///
/// @param name Name of the queue
/// @return Message queue
/// @see lockDispatcherMutex(), unlockDispatcherMutex()
//-----------------------------------------------------------------------------
extern MessageQueue *getMessageQueue(const char *name);
// @}
// @}
} // end namespace Dispatcher
// @}
#endif // _DISPATCHER_H_

View file

@ -0,0 +1,512 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "util/messaging/eventManager.h"
#include "console/consoleTypes.h"
#include "console/consoleInternal.h"
IMPLEMENT_CONOBJECT( EventManager );
ConsoleDocClass( EventManager,
"@brief The EventManager class is a wrapper for the standard messaging system.\n\n"
"It provides functionality for management of event queues, events, and "
"subscriptions. Creating an EventManager is as simple as calling new EventManager "
"and specifying a queue name.\n\n"
"@tsexample\n"
"// Create the EventManager.\n"
"$MyEventManager = new EventManager() { queue = \"MyEventManager\"; };\n\n"
"// Create an event.\n"
"$MyEventManager.registerEvent( \"SomeCoolEvent\" );\n\n"
"// Create a listener and subscribe.\n"
"$MyListener = new ScriptMsgListener() { class = MyListener; };\n"
"$MyEventManager.subscribe( $MyListener, \"SomeCoolEvent\" );\n\n"
"function MyListener::onSomeCoolEvent( %this, %data )\n"
"{\n"
" echo( \"onSomeCoolEvent Triggered\" );\n"
"}\n\n"
"// Trigger the event.\n"
"$MyEventManager.postEvent( \"SomeCoolEvent\", \"Data\" );\n"
"@endtsexample\n\n"
"@see ScriptMsgListener\n\n"
"@ingroup Messaging\n"
);
Vector<EventManager*> EventManager::smEventManagers;
//-----------------------------------------------------------------------------
/// Gets a list of all listeners of a specific event type and executes a
/// callback on each one.
///
/// @param event The name of the event that was triggered.
/// @param data The data associated with the event.
/// @return true to allow other listeners to receive the event, false otherwise
//-----------------------------------------------------------------------------
// CodeReview [tom, 2/20/2007] There seemed to be a little confusion on the return value here.
// It is not a "successfully dispatched" value, it is used to prevent other
// listeners from receiving the message. Using the event manager this probably
// didn't matter since there was only one listener, however it would cause
// problems if more then one listener is registered with the queue.
bool EventManagerListener::onMessageReceived( StringTableEntry queue, const char* event, const char* data )
{
Vector<Subscriber>* subscribers = mSubscribers.retreive( event );
if( subscribers == NULL )
return true;
for( Vector<Subscriber>::iterator iter = subscribers->begin(); iter != subscribers->end(); iter++ )
{
if( iter->listener == NULL )
{
subscribers->erase_fast( iter -- );
continue;
}
if (!iter->removeFlag)
{
iter->callDepth++;
Con::executef( iter->listener, iter->callback, data );
iter->callDepth--;
if (iter->removeFlag && iter->callDepth==0)
{
subscribers->erase_fast(iter--);
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
EventManager::EventManager() : mQueue( NULL )
{
addEventManager( this );
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
EventManager::~EventManager()
{
setMessageQueue( "" );
unregisterAllEvents();
removeEventManager( this );
}
//-----------------------------------------------------------------------------
// initPersistFields
//-----------------------------------------------------------------------------
void EventManager::initPersistFields()
{
addProtectedField( "queue", TypeString, Offset( mQueue, EventManager ), &_setMessageQueue, &defaultProtectedGetFn, "List of events currently waiting" );
}
//-----------------------------------------------------------------------------
/// Registers the message queue and listener with the messaging system.
///
/// @param queue The name of the queue. Set to "" to destroy the queue.
//-----------------------------------------------------------------------------
void EventManager::setMessageQueue( const char* queue )
{
// If a queue is already registered, unregister it.
if( mQueue && Dispatcher::isQueueRegistered( mQueue ) )
{
unregisterAllEvents();
Dispatcher::unregisterMessageListener( mQueue, &mListener );
Dispatcher::unregisterMessageQueue( mQueue );
}
// Register the new queue.
if( queue && *queue )
{
Dispatcher::registerMessageQueue( queue );
Dispatcher::registerMessageListener( queue, &mListener );
mQueue = StringTable->insert( queue );
}
}
//-----------------------------------------------------------------------------
/// Determines whether or not an event is registered with the EventManager.
///
/// @param event the event to check.
//-----------------------------------------------------------------------------
bool EventManager::isRegisteredEvent( const char* event )
{
// Iterate over the event list.
StringTableEntry eventName = StringTable->insert( event );
for( Vector<StringTableEntry>::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ )
{
// Found.
if( *iter == eventName )
return true;
}
// Not found.
return false;
}
//-----------------------------------------------------------------------------
/// Register an event with the EventManager.
///
/// @param event The event to register.
/// @return Whether or not the event was successfully registered.
//-----------------------------------------------------------------------------
bool EventManager::registerEvent( const char* event )
{
// Make sure the event has not been registered yet.
if( isRegisteredEvent( event ) )
{
Con::warnf( "EventManager::registerEvent - event %s already registered", event );
return false;
}
// Add to the event list.
mEvents.push_back( StringTable->insert( event ) );
// Create a list of subscribers for this event.
mListener.mSubscribers.insert( new Vector<EventManagerListener::Subscriber>, event );
return true;
}
//-----------------------------------------------------------------------------
/// Removes all events from the EventManager.
//-----------------------------------------------------------------------------
void EventManager::unregisterAllEvents()
{
// Iterate over all events.
for( Vector<StringTableEntry>::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ )
{
// Delete the subscriber list.
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.remove( *iter );
if( subscribers )
delete subscribers;
}
// Clear the event list.
mEvents.clear();
}
//-----------------------------------------------------------------------------
/// Removes an event from the EventManager.
///
/// @param event The event to remove.
//-----------------------------------------------------------------------------
void EventManager::unregisterEvent( const char* event )
{
// If the event doesn't exist, we have succeeded in removing it!
if( !isRegisteredEvent( event ) )
return;
// Iterate over all events.
StringTableEntry eventName = StringTable->insert( event );
for( Vector<StringTableEntry>::iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ )
{
// Erase the event.
if( *iter == eventName )
{
mEvents.erase_fast( iter );
break;
}
}
// Delete the subscriber list.
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.remove( event );
if( subscribers )
delete subscribers;
}
//-----------------------------------------------------------------------------
/// Post an event to the EventManager's queue.
///
/// @param event The event to post.
/// @param data Various data associated with the event.
/// @return Whether or not the message was dispatched successfully.
//-----------------------------------------------------------------------------
bool EventManager::postEvent( const char* event, const char* data )
{
AssertFatal( mQueue != NULL, "EventManager::postEvent - Queue not initialized" );
return Dispatcher::dispatchMessage( mQueue, event, data );
}
//-----------------------------------------------------------------------------
/// Subscribe a listener to an event.
///
/// @param listener The listener to subscribe.
/// @param event The event to subscribe to.
/// @param callback Optional callback name to be called when the event is
/// triggered.
/// @return Whether or not the subscription was successful.
//-----------------------------------------------------------------------------
// CodeReview [tom, 2/20/2007] The "listener" argument was an IMessageListener,
// but it was actually used as a SimObject and never a listener. Thus, it is now a SimObject.
bool EventManager::subscribe(SimObject *callbackObj, const char* event, const char* callback /*= NULL */)
{
// Make sure the event is valid.
if( !isRegisteredEvent( event ) )
{
Con::warnf( "EventManager::subscribe - %s is not a registered event.", event );
return false;
}
// Grab the callback name.
char* cb = NULL;
if( !callback || !*callback )
{
// Not specified, use default ( "onEvent" ).
S32 length = dStrlen( event ) + 5;
cb = new char[length];
dSprintf( cb, length, "on%s", event );
}
else
{
cb = new char[dStrlen(callback) + 1];
dStrcpy(cb, callback);
}
// Create the subscriber object.
EventManagerListener::Subscriber subscriber;
subscriber.listener = callbackObj;
subscriber.event = StringTable->insert( event );
subscriber.callback = StringTable->insert( cb );
subscriber.callDepth = 0;
subscriber.removeFlag = false;
delete [] cb;
// Grab the subscriber list.
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.retreive( event );
// If the event exists, there should always be a valid subscriber list.
AssertFatal( subscribers, "Invalid event subscriber list." );
// Add the subscriber.
subscribers->push_back( subscriber );
return true;
}
//-----------------------------------------------------------------------------
/// remove a listener from an event.
///
/// @param listener The listener to remove from an event callback list.
/// @param event The event to remove the listener from.
//-----------------------------------------------------------------------------
// CodeReview [tom, 2/20/2007] The "listener" argument was an IMessageListener,
// but it was actually used as a SimObject and never a listener. Thus, it is now a SimObject.
void EventManager::remove(SimObject *cbObj, const char* event)
{
// If the event doesn't exist, we have succeeded in removing it!
if( !isRegisteredEvent( event ) )
return;
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.retreive( event );
if( !subscribers )
return;
for( Vector<EventManagerListener::Subscriber>::iterator iter = subscribers->begin(); iter != subscribers->end(); iter++ )
{
// Erase the event.
if( iter->listener == cbObj )
{
if (iter->callDepth > 0)
iter->removeFlag = true;
else
subscribers->erase_fast( iter );
break;
}
}
}
void EventManager::removeAll(SimObject *cbObj)
{
// Iterate over all events.
for( Vector<StringTableEntry>::const_iterator iter1 = mEvents.begin(); iter1 != mEvents.end(); iter1++ )
{
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.retreive( *iter1 );
if( !subscribers )
continue;
for( Vector<EventManagerListener::Subscriber>::iterator iter2 = subscribers->begin(); iter2 != subscribers->end(); iter2++ )
{
// Erase the event.
if( iter2->listener == cbObj )
{
if (iter2->callDepth > 0)
iter2->removeFlag = true;
else
subscribers->erase_fast( iter2 );
break;
}
}
}
}
//-----------------------------------------------------------------------------
/// Print all registered events to the console.
//-----------------------------------------------------------------------------
void EventManager::dumpEvents()
{
Con::printf( "%s Events", mQueue );
for( Vector<StringTableEntry>::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ )
Con::printf( " %s", *iter );
}
//-----------------------------------------------------------------------------
/// Print the subscribers to an event.
///
/// @param event The event whose subscribers are to be printed.
//-----------------------------------------------------------------------------
void EventManager::dumpSubscribers( const char* event )
{
Vector<EventManagerListener::Subscriber>* subscribers = mListener.mSubscribers.retreive( event );
if( !subscribers )
{
Con::warnf( "EventManager::dumpSubscriber - %s is not a valid event.", event );
return;
}
Con::printf( "%s Subscribers", event );
for( Vector<EventManagerListener::Subscriber>::const_iterator iter = subscribers->begin(); iter != subscribers->end(); iter++ )
if( iter->listener )
{
// Grab the best fit name. This should be the first found of name, class, superclass, or class type.
Namespace* ns = iter->listener->getNamespace();
const char* name = ns ? ns->mName : getClassName() ;
Con::printf( " %s -> %s", name, iter->callback );
}
}
//-----------------------------------------------------------------------------
/// Print all registered events and their subscribers to the console.
//-----------------------------------------------------------------------------
void EventManager::dumpSubscribers()
{
Con::printf( "%s Events", mQueue );
for( Vector<StringTableEntry>::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ )
dumpSubscribers( *iter );
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
ConsoleMethod( EventManager, registerEvent, bool, 3, 3, "( String event )\n"
"Register an event with the event manager.\n"
"@param event The event to register.\n"
"@return Whether or not the event was registered successfully." )
{
return object->registerEvent( argv[2] );
}
ConsoleMethod( EventManager, unregisterEvent, void, 3, 3, "( String event )\n"
"Remove an event from the EventManager.\n"
"@param event The event to remove.\n" )
{
object->unregisterEvent( argv[2] );
}
ConsoleMethod( EventManager, isRegisteredEvent, bool, 3, 3, "( String event )\n"
"Check if an event is registered or not.\n"
"@param event The event to check.\n"
"@return Whether or not the event exists." )
{
return object->isRegisteredEvent( argv[2] );
}
ConsoleMethod( EventManager, postEvent, bool, 3, 4, "( String event, String data )\n"
"~Trigger an event.\n"
"@param event The event to trigger.\n"
"@param data The data associated with the event.\n"
"@return Whether or not the event was dispatched successfully." )
{
if( !object->getMessageQueue() || !object->getMessageQueue()[ 0 ] )
{
Con::errorf( "EventManager::postEvent - No queue name set on EventManager" );
return false;
}
return object->postEvent( argv[2], argc > 3 ? argv[3] : "" );
}
ConsoleMethod( EventManager, subscribe, bool, 4, 5, "( SimObject listener, String event, String callback )\n\n"
"Subscribe a listener to an event.\n"
"@param listener The listener to subscribe.\n"
"@param event The event to subscribe to.\n"
"@param callback Optional method name to receive the event notification. If this is not specified, \"on[event]\" will be used.\n"
"@return Whether or not the subscription was successful." )
{
// Find the listener object.
SimObject *cbObj = dynamic_cast<SimObject *>(Sim::findObject(argv[2]));
if( cbObj == NULL )
{
Con::warnf( "EventManager::subscribe - Invalid listener." );
return false;
}
return object->subscribe( cbObj, argv[3], argc > 4 ? argv[4] : NULL );
}
ConsoleMethod( EventManager, remove, void, 4, 4, "( SimObject listener, String event )\n\n"
"Remove a listener from an event.\n"
"@param listener The listener to remove.\n"
"@param event The event to be removed from.\n")
{
// Find the listener object.
SimObject * listener = dynamic_cast< SimObject * >( Sim::findObject( argv[2] ) );
if( listener )
object->remove( listener, argv[3] );
}
ConsoleMethod( EventManager, removeAll, void, 3, 3, "( SimObject listener )\n\n"
"Remove a listener from all events.\n"
"@param listener The listener to remove.\n")
{
// Find the listener object.
SimObject * listener = dynamic_cast< SimObject * >( Sim::findObject( argv[2] ) );
if( listener )
object->removeAll( listener );
}
ConsoleMethod( EventManager, dumpEvents, void, 2, 2, "()\n\n"
"Print all registered events to the console." )
{
object->dumpEvents();
}
ConsoleMethod( EventManager, dumpSubscribers, void, 2, 3, "( String event )\n\n"
"Print all subscribers to an event to the console.\n"
"@param event The event whose subscribers are to be printed. If this parameter isn't specified, all events will be dumped." )
{
if( argc > 2 )
object->dumpSubscribers( argv[2] );
else
object->dumpSubscribers();
}

View file

@ -0,0 +1,217 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _EVENTMANAGER_H_
#define _EVENTMANAGER_H_
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
#ifndef _DISPATCHER_H_
#include "util/messaging/dispatcher.h"
#endif
#ifndef _TSIMPLEHASHTABLE_H
#include "core/tSimpleHashTable.h"
#endif
//-----------------------------------------------------------------------------
/// Listener class used by the EventManager to dispatch messages to specific
/// callbacks.
//-----------------------------------------------------------------------------
class EventManagerListener : public Dispatcher::IMessageListener
{
friend class EventManager;
/// Stores subscription information for a subscriber.
struct Subscriber
{
SimObjectPtr< SimObject > listener; ///< The listener object.
StringTableEntry callback; ///< The callback to execute when the event is triggered.
StringTableEntry event; ///< The event being listened for.
U32 callDepth;
bool removeFlag;
};
/// Subscriber table hashed by event name.
SimpleHashTable< Vector<Subscriber> > mSubscribers;
public:
/// Called by the EventManager queue when an event is triggered. Calls all listeners subscribed to the triggered event.
virtual bool onMessageReceived( StringTableEntry queue, const char* event, const char* data );
virtual bool onMessageObjectReceived( StringTableEntry queue, Message *msg ) { return true; };
};
/// @addtogroup msgsys Message System
// @{
//-----------------------------------------------------------------------------
/// The EventManager class is a wrapper for the standard messaging system. It
/// provides functionality for management of event queues, events, and
/// subscriptions.
///
/// Creating an EventManager is as simple as calling <tt>new EventManager</tt>
/// and specifying a queue name.
///
/// Example Usage:
///
/// @code
/// // Create the EventManager.
/// $MyEventManager = new EventManager() { queue = "MyEventManager"; };
///
/// // Create an event.
/// $MyEventManager.registerEvent( "SomeCoolEvent" );
///
/// // Create a listener and subscribe.
/// $MyListener = new ScriptMsgListener() { class = MyListener; };
/// $MyEventManager.subscribe( $MyListener, "SomeCoolEvent" );
///
/// function MyListener::onSomeCoolEvent( %this, %data )
/// {
/// echo( "onSomeCoolEvent Triggered" );
/// }
///
/// // Trigger the event.
/// $MyEventManager.postEvent( "SomeCoolEvent", "Data" );
/// @endcode
//-----------------------------------------------------------------------------
class EventManager : public SimObject
{
typedef SimObject Parent;
private:
/// The name of the message queue.
StringTableEntry mQueue;
/// Registered events.
Vector<StringTableEntry> mEvents;
/// The event listener. Listens for all events and dispatches them to the appropriate subscribers.
EventManagerListener mListener;
/// List of all EventManagers.
static Vector<EventManager*> smEventManagers;
/// Sets the message queue.
static bool _setMessageQueue( void *obj, const char *index, const char *data )
{
static_cast<EventManager*>( obj )->setMessageQueue( data );
return false;
};
public:
DECLARE_CONOBJECT( EventManager );
EventManager();
virtual ~EventManager();
static void initPersistFields();
/// @name Properties
/// @{
StringTableEntry getMessageQueue() const { return mQueue; }
void setMessageQueue( const char* queue );
/// @}
/// @name Event Management
/// @{
/// Checks if an event is registered.
bool isRegisteredEvent( const char* eventName );
/// Registers an event.
bool registerEvent( const char* eventName );
/// Removes an event.
void unregisterEvent( const char* eventName );
/// Removes all events.
void unregisterAllEvents();
/// Triggers an event.
bool postEvent( const char* eventName, const char* data );
/// Adds a subscription to an event.
bool subscribe( SimObject *callbackObj, const char* event, const char* callback = NULL );
/// Remove a subscriber from an event.
void remove( SimObject *cbObj, const char* event );
void removeAll( SimObject *cbObj );
/// @}
/// @name Debug Output
/// @{
/// Prints all registered events to the console.
void dumpEvents();
/// Prints all subscribers to the console.
void dumpSubscribers();
/// Prints subscribers to a specific event to the console.
void dumpSubscribers( const char* event );
/// @}
/// @name Event Manager Tracking
/// @{
/// Adds an EventManager.
static void addEventManager( EventManager* em ) { smEventManagers.push_back( em ); };
/// Removes an EventManager.
static void removeEventManager( EventManager* em )
{
for( Vector<EventManager*>::iterator iter = smEventManagers.begin(); iter != smEventManagers.end(); iter++ )
{
if( *iter == em )
{
smEventManagers.erase_fast( iter );
break;
}
}
};
/// Retrieves an EventManager.
static EventManager* getEventManager( const char* name )
{
StringTableEntry queue = StringTable->insert( name );
for( Vector<EventManager*>::iterator iter = smEventManagers.begin(); iter != smEventManagers.end(); iter++ )
{
if( ( *iter )->mQueue == queue )
return *iter;
}
return NULL;
};
/// Prints all the EventManagers to the console.
static void printEventManagers()
{
for( Vector<EventManager*>::iterator iter = smEventManagers.begin(); iter != smEventManagers.end(); iter++ )
( *iter )->dumpSubscribers();
}
/// @}
};
// @}
#endif // _EVENTMANAGER_H_

View file

@ -0,0 +1,173 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "util/messaging/message.h"
#include "console/consoleTypes.h"
#include "core/util/safeDelete.h"
#include "core/stream/bitStream.h"
#include "console/engineAPI.h"
//-----------------------------------------------------------------------------
namespace Sim
{
extern SimIdDictionary *gIdDictionary;
}
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
Message::Message()
{
}
IMPLEMENT_CONOBJECT(Message);
ConsoleDocClass( Message,
"@brief Base class for messages\n\n"
"Message is the base class for C++ defined messages, and may also be used "
"in script for script defined messages if no C++ subclass is appropriate.\n\n"
"Messages are reference counted and will be automatically deleted when "
"their reference count reaches zero. When you dispatch a message, a "
"reference will be added before the dispatch and freed after the dispatch. "
"This allows for temporary messages with no additional code. If you want "
"to keep the message around, for example to dispatch it to multiple "
"queues, call addReference() before dispatching it and freeReference() "
"when you are done with it. Never delete a Message object directly "
"unless addReference() has not been called or the message has not been "
"dispatched.\n\n"
"Message IDs are pooled similarly to datablocks, with the exception that "
"IDs are reused. If you keep a message for longer than a single dispatch, "
"then you should ensure that you clear any script variables that refer "
"to it after the last freeReference(). If you don't, then it is probable "
"that the object ID will become valid again in the future and could cause "
"hard to track down bugs.\n\n"
"Messages have a unique type to simplify message handling code. For object "
"messages, the type is defined as either the script defined class name "
"or the C++ class name if no script class was defined. The message type "
"may be obtained through the getType() method.\n\n"
"By convention, any data for the message is held in script accessible "
"fields. Messages that need to be handled in C++ as well as script "
"provide the relevant data through persistent fields in a subclass of "
"Message to provide best performance on the C++ side. Script defined "
"messages usually their through dynamic fields, and may be accessed in "
"C++ using the SimObject::getDataField() method.\n\n"
"@ingroup Messaging\n"
);
//-----------------------------------------------------------------------------
IMPLEMENT_CALLBACK(Message, onAdd, void, (),(),
"Script callback when a message is first created and registered.\n\n"
"@tsexample\n"
"function Message::onAdd(%this)\n"
"{\n"
" // Perform on add code here\n"
"}\n"
"@endtsexample\n\n"
);
bool Message::onAdd()
{
if(! Parent::onAdd())
return false;
linkNamespaces();
onAdd_callback();
//Con::executef(this, "onAdd");
return true;
}
IMPLEMENT_CALLBACK(Message, onRemove, void, (),(),
"Script callback when a message is deleted.\n\n"
"@tsexample\n"
"function Message::onRemove(%this)\n"
"{\n"
" // Perform on remove code here\n"
"}\n"
"@endtsexample\n\n"
);
void Message::onRemove()
{
onRemove_callback();
//Con::executef(this, "onRemove");
unlinkNamespaces();
Parent::onRemove();
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
SimObjectId Message::getNextMessageID()
{
for(S32 i = MessageObjectIdFirst;i < MessageObjectIdLast;i++)
{
if(Sim::gIdDictionary->find(i) == NULL)
return i;
}
// Oh no ...
return 0xffffffff;
}
//-----------------------------------------------------------------------------
const char *Message::getType()
{
if(mClassName && mClassName[0] != 0)
return mClassName;
return getClassName();
}
//-----------------------------------------------------------------------------
// Console Methods
//-----------------------------------------------------------------------------
ConsoleMethod(Message, getType, const char *, 2, 2, "() Get message type (script class name or C++ class name if no script defined class)")
{
return object->getType();
}
//-----------------------------------------------------------------------------
ConsoleMethod(Message, addReference, void, 2, 2, "() Increment the reference count for this message")
{
object->addReference();
}
ConsoleMethod(Message, freeReference, void, 2, 2, "() Decrement the reference count for this message")
{
object->freeReference();
}

View file

@ -0,0 +1,145 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _MESSAGE_H_
#define _MESSAGE_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _NETCONNECTION_H_
#include "sim/netConnection.h"
#endif
// Forward Refs
class MessageQueue;
/// @addtogroup msgsys Message System
///
/// Most of the message system docs are currently just stubs and will
/// be fleshed out soon.
///
// @{
//-----------------------------------------------------------------------------
// Message Base Class
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/// @brief Base class for messages
///
/// Message is the base class for C++ defined messages, and may also be used
/// in script for script defined messages if no C++ subclass is appropriate.
///
/// Messages are reference counted and will be automatically deleted when
/// their reference count reaches zero. When you dispatch a message, a
/// reference will be added before the dispatch and freed after the dispatch.
/// This allows for temporary messages with no additional code. If you want
/// to keep the message around, for example to dispatch it to multiple
/// queues, call addReference() before dispatching it and freeReference()
/// when you are done with it. Never delete a Message object directly
/// unless addReference() has not been called or the message has not been
/// dispatched.
///
/// Message IDs are pooled similarly to datablocks, with the exception that
/// IDs are reused. If you keep a message for longer than a single dispatch,
/// then you should ensure that you clear any script variables that refer
/// to it after the last freeReference(). If you don't, then it is probable
/// that the object ID will become valid again in the future and could cause
/// hard to track down bugs.
///
/// Messages have a unique type to simplify message handling code. For object
/// messages, the type is defined as either the script defined class name
/// or the C++ class name if no script class was defined. The message type
/// may be obtained through the getType() method.
///
/// By convention, any data for the message is held in script accessible
/// fields. Messages that need to be handled in C++ as well as script
/// provide the relevant data through persistent fields in a subclass of
/// Message to provide best performance on the C++ side. Script defined
/// messages usually their through dynamic fields, and may be accessed in
/// C++ using the SimObject::getDataField() method.
//-----------------------------------------------------------------------------
class Message : public SimObject
{
typedef SimObject Parent;
public:
Message();
DECLARE_CONOBJECT(Message);
DECLARE_CALLBACK( void, onAdd, () );
DECLARE_CALLBACK( void, onRemove, () );
//-----------------------------------------------------------------------------
/// @brief Obtain next available #SimObjectId for messages
///
/// This is used in combination with the newmsg script operator to provide
/// ID pooling for messages and works similarly to datablock IDs.
///
/// By default, the 64 IDs following the datablock IDs are used for messages.
/// As message objects generally have a short life time this prevents them
/// from eating object IDs as if they haven't eaten for a year.
///
/// Note that unlike SimObjects and datablocks, Messages IDs are re-used.
/// If you store a message object in script and do not clear the variable
/// containing the object ID after freeing the message, it is probable that
/// the object ID will become valid again.
///
/// @return Next available SimObjectId
//-----------------------------------------------------------------------------
static SimObjectId getNextMessageID();
virtual bool onAdd();
virtual void onRemove();
//-----------------------------------------------------------------------------
/// @brief Get the type of the message
///
/// The message type is either the script class name or the C++ class name
/// if it has not been overridden in script. This allows easy identification
/// of message types with minimum effort.
///
/// @return Type of message
//-----------------------------------------------------------------------------
const char *getType();
//-----------------------------------------------------------------------------
/// @brief Add a reference to the reference count of this message
///
/// Use freeReference() to free the reference when you are done with it.
///
/// @see freeReference()
//-----------------------------------------------------------------------------
void addReference() { incRefCount(); }
//-----------------------------------------------------------------------------
/// @brief Free a reference to this message
///
/// @see addReference()
//-----------------------------------------------------------------------------
void freeReference() { decRefCount(); }
};
// @}
#endif // _MESSAGE_H_

View file

@ -0,0 +1,88 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "util/messaging/messageForwarder.h"
#include "console/consoleTypes.h"
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
MessageForwarder::MessageForwarder()
{
mToQueue = "";
}
MessageForwarder::~MessageForwarder()
{
}
IMPLEMENT_CONOBJECT(MessageForwarder);
ConsoleDocClass( MessageForwarder,
"@brief Forward messages from one queue to another\n\n"
"MessageForwarder is a script class that can be used to forward messages "
"from one queue to another.\n\n"
"@tsexample\n"
"%fwd = new MessageForwarder()\n"
"{\n"
" toQueue = \"QueueToSendTo\";\n"
"};\n\n"
"registerMessageListener(\"FromQueue\", %fwd);\n"
"@endtsexample\n\n"
"Where \"QueueToSendTo\" is the queue you want to forward to, and "
"\"FromQueue\" is the queue you want to forward from.\n\n"
"@ingroup Messaging\n"
);
//-----------------------------------------------------------------------------
void MessageForwarder::initPersistFields()
{
addField("toQueue", TypeCaseString, Offset(mToQueue, MessageForwarder), "Name of queue to forward to");
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
bool MessageForwarder::onMessageReceived(StringTableEntry queue, const char *event, const char *data)
{
if(*mToQueue)
Dispatcher::dispatchMessage(queue, event, data);
return Parent::onMessageReceived(queue, event, data);
}
bool MessageForwarder::onMessageObjectReceived(StringTableEntry queue, Message *msg)
{
if(*mToQueue)
Dispatcher::dispatchMessageObject(mToQueue, msg);
return Parent::onMessageObjectReceived(queue, msg);
}

View file

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/simBase.h"
#include "util/messaging/dispatcher.h"
#include "util/messaging/scriptMsgListener.h"
#ifndef _MESSAGEFORWARDER_H_
#define _MESSAGEFORWARDER_H_
/// @addtogroup msgsys Message System
// @{
//-----------------------------------------------------------------------------
/// @brief Forward messages from one queue to another
///
/// MessageForwarder is a script class that can be used to forward messages
/// from one queue to another.
///
/// <h2>Example</h2>
///
/// @code
/// %fwd = new MessageForwarder()
/// {
/// toQueue = "QueueToSendTo";
/// };
///
/// registerMessageListener("FromQueue", %fwd);
/// @endcode
///
/// Where "QueueToSendTo" is the queue you want to forward to, and
/// "FromQueue" is the queue you want to forward from.
///
//-----------------------------------------------------------------------------
class MessageForwarder : public ScriptMsgListener
{
typedef ScriptMsgListener Parent;
protected:
StringTableEntry mToQueue;
public:
MessageForwarder();
virtual ~MessageForwarder();
DECLARE_CONOBJECT(MessageForwarder);
static void initPersistFields();
virtual bool onMessageReceived(StringTableEntry queue, const char *event, const char *data);
virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg);
};
// @}
#endif // _MESSAGEFORWARDER_H_

View file

@ -0,0 +1,184 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "util/messaging/scriptMsgListener.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
ScriptMsgListener::ScriptMsgListener()
{
}
IMPLEMENT_CONOBJECT(ScriptMsgListener);
ConsoleDoc("@class ScriptMsgListener\n"
"@brief Script accessible version of Dispatcher::IMessageListener. Often used in conjunction with EventManager\n\n"
"The main use of ScriptMsgListener is to allow script to listen for"
"messages. You can subclass ScriptMsgListener in script to receive"
"the Dispatcher::IMessageListener callbacks.\n\n"
"Alternatively, you can derive from it in C++ instead of SimObject to"
"get an object that implements Dispatcher::IMessageListener with script"
"callbacks. If you need to derive from something other then SimObject,"
"then you will need to implement the Dispatcher::IMessageListener"
"interface yourself.\n\n"
"@tsexample\n"
"// Create the EventManager.\n"
"$MyEventManager = new EventManager() { queue = \"MyEventManager\"; };\n\n"
"// Create an event.\n"
"$MyEventManager.registerEvent( \"SomeCoolEvent\" );\n\n"
"// Create a listener and subscribe.\n"
"$MyListener = new ScriptMsgListener() { class = MyListener; };\n"
"$MyEventManager.subscribe( $MyListener, \"SomeCoolEvent\" );\n\n"
"function MyListener::onSomeCoolEvent( %this, %data )\n"
"{\n"
" echo( \"onSomeCoolEvent Triggered\" );\n"
"}\n\n"
"// Trigger the event.\n"
"$MyEventManager.postEvent( \"SomeCoolEvent\", \"Data\" );\n"
"@endtsexample\n\n"
"@ingroup Messaging\n"
);
//-----------------------------------------------------------------------------
IMPLEMENT_CALLBACK(ScriptMsgListener, onAdd, void, (),(),
"Script callback when a listener is first created and registered.\n\n"
"@tsexample\n"
"function ScriptMsgListener::onAdd(%this)\n"
"{\n"
" // Perform on add code here\n"
"}\n"
"@endtsexample\n\n"
);
bool ScriptMsgListener::onAdd()
{
if(! Parent::onAdd())
return false;
linkNamespaces();
onAdd_callback();
//Con::executef(this, "onAdd");
return true;
}
IMPLEMENT_CALLBACK(ScriptMsgListener, onRemove, void, (),(),
"Script callback when a listener is deleted.\n\n"
"@tsexample\n"
"function ScriptMsgListener::onRemove(%this)\n"
"{\n"
" // Perform on remove code here\n"
"}\n"
"@endtsexample\n\n"
);
void ScriptMsgListener::onRemove()
{
onRemove_callback();
//Con::executef(this, "onRemove");
unlinkNamespaces();
Parent::onRemove();
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
IMPLEMENT_CALLBACK( ScriptMsgListener, onMessageReceived, bool, ( const char* queue, const char* event, const char* data ), ( queue, event, data ),
"Called when the listener has received a message.\n"
"@param queue The name of the queue the message was dispatched to\n"
"@param event The name of the event (function) that was triggered\n"
"@param data The data (parameters) for the message\n\n"
"@return false to prevent other listeners receiving this message, true otherwise\n" );
bool ScriptMsgListener::onMessageReceived(StringTableEntry queue, const char* event, const char* data)
{
return onMessageReceived_callback(queue, event, data);
//return dAtob(Con::executef(this, "onMessageReceived", queue, event, data));
}
IMPLEMENT_CALLBACK( ScriptMsgListener, onMessageObjectReceived, bool, ( const char* queue, Message *msg ), ( queue, msg ),
"Called when a message object (not just the message data) is passed to a listener.\n"
"@param queue The name of the queue the message was dispatched to\n"
"@param msg The message object\n"
"@return false to prevent other listeners receiving this message, true otherwise\n"
"@see Message\n"
"@see onMessageReceived");
bool ScriptMsgListener::onMessageObjectReceived(StringTableEntry queue, Message *msg)
{
return onMessageObjectReceived_callback(queue, msg);
//return dAtob(Con::executef(this, "onMessageObjectReceived", queue, Con::getIntArg(msg->getId())));
}
//-----------------------------------------------------------------------------
IMPLEMENT_CALLBACK( ScriptMsgListener, onAddToQueue, void, ( const char* queue), ( queue),
"@brief Callback for when the listener is added to a queue\n\n"
"The default implementation of onAddToQueue() and onRemoveFromQueue() "
"provide tracking of the queues this listener is added to through the "
"mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue() "
"should ensure they call the parent implementation in any overrides.\n"
"@param queue The name of the queue that the listener added to\n"
"@see onRemoveFromQueue()");
void ScriptMsgListener::onAddToQueue(StringTableEntry queue)
{
//Con::executef(this, "onAddToQueue", queue);
onAddToQueue_callback(queue);
IMLParent::onAddToQueue(queue);
}
/// @brief Callback for when the listener is removed from a queue
///
/// The default implementation of onAddToQueue() and onRemoveFromQueue()
/// provide tracking of the queues this listener is added to through the
/// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue()
/// should ensure they call the parent implementation in any overrides.
///
/// @param queue The name of the queue the listener was removed from
/// @see onAddToQueue()
//-----------------------------------------------------------------------------
IMPLEMENT_CALLBACK( ScriptMsgListener, onRemoveFromQueue, void, ( const char* queue), ( queue),
"@brief Callback for when the listener is removed from a queue\n\n"
"The default implementation of onAddToQueue() and onRemoveFromQueue() "
"provide tracking of the queues this listener is added to through the "
"mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue() "
"should ensure they call the parent implementation in any overrides.\n"
"@param queue The name of the queue that the listener was removed from\n"
"@see onAddToQueue()");
void ScriptMsgListener::onRemoveFromQueue(StringTableEntry queue)
{
//Con::executef(this, "onRemoveFromQueue", queue);
onRemoveFromQueue_callback(queue);
IMLParent::onRemoveFromQueue(queue);
}

View file

@ -0,0 +1,83 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/simBase.h"
#ifndef _SCRIPTMSGLISTENER_H_
#define _SCRIPTMSGLISTENER_H_
#ifndef _DISPATCHER_H_
#include "util/messaging/dispatcher.h"
#endif
/// @addtogroup msgsys Message System
// @{
//-----------------------------------------------------------------------------
/// @brief Script accessible version of Dispatcher::IMessageListener
///
/// The main use of ScriptMsgListener is to allow script to listen for
/// messages. You can subclass ScriptMsgListener in script to receive
/// the Dispatcher::IMessageListener callbacks.
///
/// Alternatively, you can derive from it in C++ instead of SimObject to
/// get an object that implements Dispatcher::IMessageListener with script
/// callbacks. If you need to derive from something other then SimObject,
/// then you will need to implement the Dispatcher::IMessageListener
/// interface yourself.
//-----------------------------------------------------------------------------
class ScriptMsgListener : public SimObject, public virtual Dispatcher::IMessageListener
{
typedef SimObject Parent;
typedef Dispatcher::IMessageListener IMLParent;
public:
ScriptMsgListener();
DECLARE_CONOBJECT(ScriptMsgListener);
DECLARE_CALLBACK( void, onAdd, () );
DECLARE_CALLBACK( void, onRemove, () );
DECLARE_CALLBACK( bool, onMessageReceived, ( const char* queue, const char* event, const char* data ) );
DECLARE_CALLBACK( bool, onMessageObjectReceived, ( const char* queue, Message *msg ) );
DECLARE_CALLBACK( void, onAddToQueue, ( const char* queue ) );
DECLARE_CALLBACK( void, onRemoveFromQueue, ( const char* queue ) );
///////////////////////////////////////////////////////////////////////
virtual bool onAdd();
virtual void onRemove();
///////////////////////////////////////////////////////////////////////
virtual bool onMessageReceived(StringTableEntry queue, const char* event, const char* data);
virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg);
virtual void onAddToQueue(StringTableEntry queue);
virtual void onRemoveFromQueue(StringTableEntry queue);
};
// @}
#endif // _SCRIPTMSGLISTENER_H_