Browse Source

Object to Object Events

This new feature is designed to replace the old event manager.  Now any
object can post and listen to events from any other object directly.
There's no need to step up a queue or register events.  Simply set one
object to listen to another and then post events as needed.
Peter Robinson 9 years ago
parent
commit
757e6e7cb4

+ 5 - 5
engine/source/2d/sceneobject/SceneObjectList.cc

@@ -28,7 +28,7 @@
 
 
 void SceneObjectList::pushBack(SceneObject* obj)
 void SceneObjectList::pushBack(SceneObject* obj)
 {
 {
-	if (find(begin(),end(),obj) == end())
+   if (::find(begin(), end(), obj) == end())
 		push_back(obj);
 		push_back(obj);
 }	
 }	
 
 
@@ -36,7 +36,7 @@ void SceneObjectList::pushBack(SceneObject* obj)
 
 
 void SceneObjectList::pushBackForce(SceneObject* obj)
 void SceneObjectList::pushBackForce(SceneObject* obj)
 {
 {
-	iterator itr = find(begin(),end(),obj);
+	iterator itr = ::find(begin(),end(),obj);
 	if (itr == end()) 
 	if (itr == end()) 
 	{
 	{
 		push_back(obj);
 		push_back(obj);
@@ -54,7 +54,7 @@ void SceneObjectList::pushBackForce(SceneObject* obj)
 
 
 void SceneObjectList::pushFront(SceneObject* obj)
 void SceneObjectList::pushFront(SceneObject* obj)
 {
 {
-	if (find(begin(),end(),obj) == end())
+	if (::find(begin(),end(),obj) == end())
 		push_front(obj);
 		push_front(obj);
 }	
 }	
 
 
@@ -62,7 +62,7 @@ void SceneObjectList::pushFront(SceneObject* obj)
 
 
 void SceneObjectList::remove(SceneObject* obj)
 void SceneObjectList::remove(SceneObject* obj)
 {
 {
-	iterator ptr = find(begin(),end(),obj);
+	iterator ptr = ::find(begin(),end(),obj);
 	if (ptr != end()) 
 	if (ptr != end()) 
 		erase(ptr);
 		erase(ptr);
 }
 }
@@ -71,7 +71,7 @@ void SceneObjectList::remove(SceneObject* obj)
 
 
 void SceneObjectList::removeStable(SceneObject* obj)
 void SceneObjectList::removeStable(SceneObject* obj)
 {
 {
-	iterator ptr = find(begin(),end(),obj);
+	iterator ptr = ::find(begin(),end(),obj);
 	if (ptr != end()) 
 	if (ptr != end()) 
 		erase(ptr);
 		erase(ptr);
 }
 }

+ 3 - 3
engine/source/collection/simpleHashTable.h

@@ -79,17 +79,17 @@ public:
 
 
 template <class T> inline void SimpleHashTable<T>::insert(T* pObject, U8 *key, U32 keyLen)
 template <class T> inline void SimpleHashTable<T>::insert(T* pObject, U8 *key, U32 keyLen)
 {
 {
-   Parent::insert(pObject, hash(key, keyLen, 0));
+   Parent::insert(pObject, ::hash(key, keyLen, 0));
 }
 }
 
 
 template <class T> inline T* SimpleHashTable<T>::remove(U8 *key, U32 keyLen)
 template <class T> inline T* SimpleHashTable<T>::remove(U8 *key, U32 keyLen)
 {
 {
-   return Parent::remove(hash(key, keyLen, 0));
+   return Parent::remove(::hash(key, keyLen, 0));
 }
 }
 
 
 template <class T> inline T* SimpleHashTable<T>::retrieve(U8 *key, U32 keyLen)
 template <class T> inline T* SimpleHashTable<T>::retrieve(U8 *key, U32 keyLen)
 {
 {
-   return Parent::retrieve(hash(key, keyLen, 0));
+   return Parent::retrieve(::hash(key, keyLen, 0));
 }
 }
 
 
 template <class T> inline void SimpleHashTable<T>::insert(T* pObject, const char *key)
 template <class T> inline void SimpleHashTable<T>::insert(T* pObject, const char *key)

+ 5 - 5
engine/source/sim/SimObjectList.cc

@@ -28,7 +28,7 @@
 
 
 void SimObjectList::pushBack(SimObject* obj)
 void SimObjectList::pushBack(SimObject* obj)
 {
 {
-   if (find(begin(),end(),obj) == end())
+   if (::find(begin(),end(),obj) == end())
       push_back(obj);
       push_back(obj);
 }	
 }	
 
 
@@ -36,7 +36,7 @@ void SimObjectList::pushBack(SimObject* obj)
 
 
 void SimObjectList::pushBackForce(SimObject* obj)
 void SimObjectList::pushBackForce(SimObject* obj)
 {
 {
-   iterator itr = find(begin(),end(),obj);
+   iterator itr = ::find(begin(),end(),obj);
    if (itr == end()) 
    if (itr == end()) 
    {
    {
       push_back(obj);
       push_back(obj);
@@ -55,7 +55,7 @@ void SimObjectList::pushBackForce(SimObject* obj)
 
 
 void SimObjectList::pushFront(SimObject* obj)
 void SimObjectList::pushFront(SimObject* obj)
 {
 {
-   if (find(begin(),end(),obj) == end())
+   if (::find(begin(),end(),obj) == end())
       push_front(obj);
       push_front(obj);
 }	
 }	
 
 
@@ -63,7 +63,7 @@ void SimObjectList::pushFront(SimObject* obj)
 
 
 void SimObjectList::remove(SimObject* obj)
 void SimObjectList::remove(SimObject* obj)
 {
 {
-   iterator ptr = find(begin(),end(),obj);
+   iterator ptr = ::find(begin(),end(),obj);
    if (ptr != end()) 
    if (ptr != end()) 
       erase(ptr);
       erase(ptr);
 }
 }
@@ -72,7 +72,7 @@ void SimObjectList::remove(SimObject* obj)
 
 
 void SimObjectList::removeStable(SimObject* obj)
 void SimObjectList::removeStable(SimObject* obj)
 {
 {
-   iterator ptr = find(begin(),end(),obj);
+   iterator ptr = ::find(begin(),end(),obj);
    if (ptr != end()) 
    if (ptr != end()) 
       erase(ptr);
       erase(ptr);
 }
 }

+ 97 - 0
engine/source/sim/simObject.cc

@@ -31,6 +31,7 @@
 #include "console/ConsoleTypeValidators.h"
 #include "console/ConsoleTypeValidators.h"
 
 
 #include "simObject_ScriptBinding.h"
 #include "simObject_ScriptBinding.h"
+#include <algorithm>
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
@@ -67,6 +68,7 @@ SimObject::SimObject()
     mSuperClassName          = NULL;
     mSuperClassName          = NULL;
     mProgenitorFile          = CodeBlock::getCurrentCodeBlockFullPath();
     mProgenitorFile          = CodeBlock::getCurrentCodeBlockFullPath();
     mPeriodicTimerID         = 0;
     mPeriodicTimerID         = 0;
+    bIsEventRaised           = false;
 }
 }
 
 
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
@@ -1382,6 +1384,101 @@ void SimObject::setSuperClassNamespace( const char* superClassNamespace )
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
+void SimObject::addListener(std::string objID)
+{
+   for (auto iter = mListenerList.begin(); iter != mListenerList.end(); ++iter)
+   {
+      if (iter->objID == objID)
+      {
+         iter->doomed = false;
+         return;
+      }
+   }
+
+   OtoListener listener = OtoListener();
+   listener.objID = objID;
+   listener.doomed = false;
+   mListenerList.push_back(listener);
+}
+
+//-----------------------------------------------------------------------------
+
+void SimObject::removeListener(std::string objID)
+{
+   for (auto iter = mListenerList.begin(); iter != mListenerList.end(); ++iter)
+   {
+      if (iter->objID == objID)
+      {
+         iter->doomed = true;
+      }
+   }
+   
+   if (!bIsEventRaised)
+   {
+      mListenerList.erase(std::remove_if(mListenerList.begin(), mListenerList.end(), [](OtoListener listener){ return listener.doomed; }), mListenerList.end());
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+void SimObject::removeAllListeners()
+{
+   if (bIsEventRaised)
+   {
+      for (auto iter = mListenerList.begin(); iter != mListenerList.end(); ++iter)
+      {
+         iter->doomed = true;
+      }
+   }
+   else
+   {
+      mListenerList.clear();
+   }
+}
+
+//-----------------------------------------------------------------------------
+
+void SimObject::postEvent(std::string eventName, std::string data)
+{
+   std::string onEvent = "on" + eventName;
+   if (mListenerList.empty())
+   {
+      return;
+   }
+
+   if (bIsEventRaised)
+   {
+      Con::warnf("SimObject::postEvent() - To avoid circular events, you cannot raise the event '%s' until a previous event has finished.", eventName.c_str());
+      return;
+   }
+
+   bIsEventRaised = true;
+   for (auto iter = mListenerList.begin(); iter != mListenerList.end(); ++iter)
+   {
+      SimObject* pSimObject = dynamic_cast<SimObject*>(Sim::findObject(iter->objID.c_str()));
+
+      // Did we find the object?
+      if (pSimObject)
+      {
+         if (!iter->doomed && pSimObject->isMethod(onEvent.c_str()))
+         {
+            Con::executef(pSimObject, 3, onEvent.c_str(), data.c_str());
+         }
+      }
+      else
+      {
+         //it must have been deleted
+         iter->doomed = true;
+      }
+   }
+   bIsEventRaised = false;
+
+   //now to remove all doomed listeners
+   mListenerList.erase(std::remove_if(mListenerList.begin(), mListenerList.end(), [](OtoListener listener){ return listener.doomed; }), mListenerList.end());
+}
+
+//-----------------------------------------------------------------------------
+
 static S32 QSORT_CALLBACK compareFields(const void* a,const void* b)
 static S32 QSORT_CALLBACK compareFields(const void* a,const void* b)
 {
 {
    const AbstractClassRep::Field* fa = *((const AbstractClassRep::Field**)a);
    const AbstractClassRep::Field* fa = *((const AbstractClassRep::Field**)a);

+ 20 - 0
engine/source/sim/simObject.h

@@ -37,6 +37,8 @@
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
+using namespace std;
+#include <vector>
 typedef U32 SimObjectId;
 typedef U32 SimObjectId;
 class SimGroup;
 class SimGroup;
 
 
@@ -705,6 +707,24 @@ public:
 
 
     /// @}
     /// @}
 
 
+    /// @Object to Object Events
+    /// @{
+private:
+    struct OtoListener {
+        bool doomed;
+        std::string objID;
+    };
+    std::vector<OtoListener> mListenerList;
+    bool bIsEventRaised;
+public:
+    void addListener(std::string objID);
+    void removeListener(std::string objID);
+    void removeAllListeners();
+    void postEvent(std::string eventName, std::string data);
+
+    /// @}
+
+public:
     virtual void            dump();
     virtual void            dump();
     virtual void            dumpClassHierarchy();
     virtual void            dumpClassHierarchy();
 
 

+ 117 - 0
engine/source/sim/simObject_ScriptBinding.h

@@ -1018,4 +1018,121 @@ ConsoleMethodWithDocs(SimObject,schedule, ConsoleInt, 4, 0, (time , command , [a
 
 
 /*! @} */ // member group Timer Events
 /*! @} */ // member group Timer Events
 
 
+/*! @name member group Object to Object Events
+Raise events for listening objects to consume.
+@{
+*/
+
+/*! Starts listening to another object.
+   @param SimObject The object that will be posting events.
+@return No return value.
+*/
+ConsoleMethodWithDocs(SimObject, startListening, ConsoleVoid, 3, 3, (SimObject))
+{
+   // Find the specified object.
+   SimObject* pSimObject = dynamic_cast<SimObject*>(Sim::findObject(argv[2]));
+
+   // Did we find the object?
+   if (!pSimObject)
+   {
+      // No, so warn.
+      Con::warnf("SimObject::startListening() - Could not find the specified object '%s'.", argv[2]);
+      return;
+   }
+
+   // Start Listening
+   pSimObject->addListener(object->getIdString());
+}
+
+/*! Stops listening to another object.
+@param SimObject The object that will be posting events.
+@return No return value.
+*/
+ConsoleMethodWithDocs(SimObject, stopListening, ConsoleVoid, 3, 3, (SimObject))
+{
+   // Find the specified object.
+   SimObject* pSimObject = dynamic_cast<SimObject*>(Sim::findObject(argv[2]));
+
+   // Did we find the object?
+   if (!pSimObject)
+   {
+      // No, so warn.
+      Con::warnf("SimObject::stopListening() - Could not find the specified object '%s'.", argv[2]);
+      return;
+   }
+
+   // Stop Listening
+   pSimObject->removeListener(object->getIdString());
+}
+
+/*! Adds an object so that it receives events from this object.
+@param SimObject The object that will be listening to events.
+@return No return value.
+*/
+ConsoleMethodWithDocs(SimObject, addListener, ConsoleVoid, 3, 3, (SimObject))
+{
+   // Find the specified object.
+   SimObject* pSimObject = dynamic_cast<SimObject*>(Sim::findObject(argv[2]));
+
+   // Did we find the object?
+   if (!pSimObject)
+   {
+      // No, so warn.
+      Con::warnf("SimObject::addListener() - Could not find the specified object '%s'.", argv[2]);
+      return;
+   }
+
+   // Start Listening
+   object->addListener(pSimObject->getIdString());
+}
+
+/*! Removes an object so that it no longer receives events from this object.
+@param SimObject The object that will stop listening to events.
+@return No return value.
+*/
+ConsoleMethodWithDocs(SimObject, removeListener, ConsoleVoid, 3, 3, (SimObject))
+{
+   // Find the specified object.
+   SimObject* pSimObject = dynamic_cast<SimObject*>(Sim::findObject(argv[2]));
+
+   // Did we find the object?
+   if (!pSimObject)
+   {
+      // No, so warn.
+      Con::warnf("SimObject::removeListener() - Could not find the specified object '%s'.", argv[2]);
+      return;
+   }
+
+   // Start Listening
+   object->removeListener(pSimObject->getIdString());
+}
+
+/*! Removes all listeners from this object.
+@return No return value.
+*/
+ConsoleMethodWithDocs(SimObject, removeAllListeners, ConsoleVoid, 2, 2, ())
+{
+   // Start Listening
+   object->removeAllListeners();
+}
+
+/*! Raises an event on all listening objects.
+    @param eventName The name of the event to raise. The actual function called on listeners will begin with "on" followed by the event name.
+    @param data Any data that should be passed on to the listeners.
+@return No return value.
+*/
+ConsoleMethodWithDocs(SimObject, postEvent, ConsoleVoid, 3, 4, (String eventName, String data))
+{
+   if (argc < 3)
+   {
+      Con::warnf("SimObject::postEvent() - Invalid number of parameters. You must include an Event Name.");
+      return;
+   }
+
+   // Start Listening
+   object->postEvent(argv[2], argc > 3 ? argv[3] : "");
+}
+
+/*! @} */ // member group Object to Object Events
+
 ConsoleMethodRootGroupEndWithDocs(SimObject)
 ConsoleMethodRootGroupEndWithDocs(SimObject)