//----------------------------------------------------------------------------- // 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 _SIMSET_H_ #define _SIMSET_H_ #ifndef _SIMOBJECT_H_ #include "console/simObject.h" #endif #ifndef _SIMOBJECTLIST_H_ #include "console/simObjectList.h" #endif #ifndef _SIMDICTIONARY_H_ #include "console/simDictionary.h" #endif #ifndef _TSIGNAL_H_ #include "core/util/tSignal.h" #endif #include "persistence/taml/tamlChildren.h" //--------------------------------------------------------------------------- /// A set of SimObjects. /// /// It is often necessary to keep track of an arbitrary set of SimObjects. /// For instance, Torque's networking code needs to not only keep track of /// the set of objects which need to be ghosted, but also the set of objects /// which must always be ghosted. It does this by working with two /// sets. The first of these is the RootGroup (which is actually a SimGroup) /// and the second is the GhostAlwaysSet, which contains objects which must /// always be ghosted to the client. /// /// Some general notes on SimSets: /// - Membership is not exclusive. A SimObject may be a member of multiple /// SimSets. /// - A SimSet does not destroy subobjects when it is destroyed. /// - A SimSet may hold an arbitrary number of objects. /// /// Using SimSets, the code to work with these two sets becomes /// relatively straightforward: /// /// @code /// // (Example from netObject.cc) /// // To iterate over all the objects in the Sim: /// for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj) /// { /// NetObject* nobj = dynamic_cast(*obj); /// /// if (nobj) /// { /// // ... do things ... /// } /// } /// /// // (Example from netGhost.cc) /// // To iterate over the ghostAlways set. /// SimSet* ghostAlwaysSet = Sim::getGhostAlwaysSet(); /// SimSet::iterator i; /// /// U32 sz = ghostAlwaysSet->size(); /// S32 j; /// /// for(i = ghostAlwaysSet->begin(); i != ghostAlwaysSet->end(); i++) /// { /// NetObject *obj = (NetObject *)(*i); /// /// /// ... do things with obj... /// } /// @endcode /// class SimSet : public SimObject, public TamlChildren { public: typedef SimObject Parent; typedef SimObject Children; enum SetModification { SetCleared, SetObjectAdded, SetObjectRemoved }; /// Signal for letting observers know when objects are added to or removed from /// the set. /// /// @param modification In what way the set has been modified. /// @param set The set that has been modified. /// @param object If #modification is #SetObjectAdded or #SetObjectRemoved, this is /// the object that has been added or removed. Otherwise NULL. typedef Signal< void( SetModification modification, SimSet* set, SimObject* object ) > SetModificationSignal; protected: SimObjectList mObjectList; void *mMutex; /// Signal that is triggered when objects are added or removed from the set. SetModificationSignal mSetModificationSignal; /// @name Callbacks /// @{ DECLARE_CALLBACK( void, onObjectAdded, ( SimObject* object ) ); DECLARE_CALLBACK( void, onObjectRemoved, ( SimObject* object ) ); /// @} public: SimSet(); ~SimSet(); /// Return the signal that is triggered when an object is added to or removed /// from the set. const SetModificationSignal& getSetModificationSignal() const { return mSetModificationSignal; } SetModificationSignal& getSetModificationSignal() { return mSetModificationSignal; } /// @name STL Interface /// @{ /// typedef SimObjectList::iterator iterator; typedef SimObjectList::value_type value; SimObject* front() { return mObjectList.front(); } SimObject* first() { return mObjectList.first(); } SimObject* last() { return mObjectList.last(); } bool empty() const { return mObjectList.empty(); } S32 size() const { return mObjectList.size(); } iterator begin() { return mObjectList.begin(); } iterator end() { return mObjectList.end(); } value operator[] (S32 index) { return mObjectList[U32(index)]; } inline iterator find( iterator first, iterator last, SimObject *obj) { return T3D::find(first, last, obj); } inline iterator find(SimObject *obj) { return T3D::find(begin(), end(), obj); } /// Reorder the position of "obj" to either be the last object in the list or, if /// "target" is given, to come before "target" in the list of children. virtual bool reOrder( SimObject *obj, SimObject *target=0 ); /// Return the object at the given index. SimObject* at(S32 index) const { return mObjectList.at(index); } /// Remove all objects from this set. virtual void clear(); /// @} /// @name Set Management /// @{ /// Add the given object to the set. /// @param object Object to add to the set. virtual void addObject( SimObject* object ); /// Remove the given object from the set. /// @param object Object to remove from the set. virtual void removeObject( SimObject* object ); /// Add the given object to the end of the object list of this set. /// @param object Object to add to the set. virtual void pushObject( SimObject* object ); /// Return true if this set accepts the given object as a child. /// This method should be overridden for set classes that restrict membership. virtual bool acceptsAsChild( SimObject* object ) const { return true; } /// Deletes all the objects in the set. void deleteAllObjects(); /// Remove an object from the end of the list. virtual void popObject(); void bringObjectToFront(SimObject* obj) { reOrder(obj, front()); } void pushObjectToBack(SimObject* obj) { reOrder(obj, NULL); } /// Performs a sort of the objects in the set using a script /// callback function to do the comparison. /// /// An example script sort callback: /// /// @code /// function sortByName( %object1, %object2 ) /// { /// return strcmp( %object1.getName(), %object2.getName() ); /// } /// @endcode /// /// Note: You should never modify the SimSet itself while in /// the sort callback function as it can cause a deadlock. /// void scriptSort( const String &scriptCallbackFn ); /// @} void callOnChildren( const String &method, S32 argc, ConsoleValue argv[], bool executeOnChildGroups = true ); /// Return the number of objects in this set as well as all sets that are contained /// in this set and its children. /// /// @note The child sets themselves count towards the total too. U32 sizeRecursive(); virtual SimObject* findObjectByInternalName(StringTableEntry internalName, bool searchChildren = false); SimObject* findObjectByLineNumber(const char* fileName, S32 declarationLine, bool searchChildren = false); /// Find the given object in this set. Returns NULL if the object /// is not part of this set. SimObject* findObject( SimObject* object ); /// Add all child objects ( including children of children ) to the foundObjects /// Vector which are of type T. template< class T > void findObjectByType( Vector &foundObjects ); /// Add all child objects ( including children of children ) to the foundObjects /// Vector which are of type T and for which DecideAddObjectCallback return true; template< class T > void findObjectByCallback( bool ( *fn )( T* ), Vector& foundObjects ); SimObject* getRandom(); inline void lock() { #ifdef TORQUE_MULTITHREAD Mutex::lockMutex(mMutex); #endif } void unlock() { #ifdef TORQUE_MULTITHREAD Mutex::unlockMutex(mMutex); #endif } #ifdef TORQUE_DEBUG_GUARD inline void _setVectorAssoc( const char *file, const U32 line ) { mObjectList.setFileAssociation( file, line ); } #endif // SimObject. DECLARE_CONOBJECT( SimSet ); virtual void onRemove(); virtual void onDeleteNotify(SimObject *object); virtual SimObject* findObject( const char* name ); virtual void write(Stream &stream, U32 tabStop, U32 flags = 0); virtual bool writeObject(Stream *stream); virtual bool readObject(Stream *stream); virtual SimSet* clone(); // TamlChildren virtual U32 getTamlChildCount(void) const { return (U32)size(); } virtual SimObject* getTamlChild(const U32 childIndex) const { // Sanity! AssertFatal(childIndex < (U32)size(), "SimSet::getTamlChild() - Child index is out of range."); // For when the assert is not used. if (childIndex >= (U32)size()) return NULL; return at(childIndex); } virtual void addTamlChild(SimObject* pSimObject) { // Sanity! AssertFatal(pSimObject != NULL, "SimSet::addTamlChild() - Cannot add a NULL child object."); addObject(pSimObject); } }; #ifdef TORQUE_DEBUG_GUARD # define SIMSET_SET_ASSOCIATION( x ) x._setVectorAssoc( __FILE__, __LINE__ ) #else # define SIMSET_SET_ASSOCIATION( x ) #endif template< class T > void SimSet::findObjectByType( Vector &foundObjects ) { T *curObj; SimSet *curSet; lock(); // Loop through our child objects. SimObjectList::iterator itr = mObjectList.begin(); for ( ; itr != mObjectList.end(); itr++ ) { curObj = dynamic_cast( *itr ); curSet = dynamic_cast( *itr ); // If child object is a set, call recursively into it. if ( curSet && curSet->size() != 0) curSet->findObjectByType( foundObjects ); // Add this child object if appropriate. if ( curObj ) foundObjects.push_back( curObj ); } // Add this object if appropriate. curObj = dynamic_cast(this); if ( curObj ) foundObjects.push_back( curObj ); unlock(); } template< class T > void SimSet::findObjectByCallback( bool ( *fn )( T* ), Vector &foundObjects ) { T *curObj; SimSet *curSet; lock(); // Loop through our child objects. SimObjectList::iterator itr = mObjectList.begin(); for ( ; itr != mObjectList.end(); itr++ ) { curObj = dynamic_cast( *itr ); curSet = dynamic_cast( *itr ); // If child object is a set, call recursively into it. if ( curSet ) curSet->findObjectByCallback( fn, foundObjects ); // Add this child object if appropriate. if ( curObj && ( fn == NULL || fn( curObj ) ) ) foundObjects.push_back( curObj ); } // Add this object if appropriate. curObj = dynamic_cast(this); if ( curObj && ( fn == NULL || fn( curObj ) ) ) foundObjects.push_back( curObj ); unlock(); } /// An iterator that recursively and exhaustively traverses the contents /// of a SimSet. /// /// @see SimSet class SimSetIterator { protected: struct Entry { SimSet* set; SimSet::iterator itr; }; class Stack: public Vector { public: void push_back(SimSet*); }; Stack stack; public: SimSetIterator(SimSet*); SimObject* operator++(); SimObject* operator*() { return stack.empty()? 0: *stack.last().itr; } }; //--------------------------------------------------------------------------- /// A group of SimObjects. /// /// A SimGroup is a stricter form of SimSet. SimObjects may only be a member /// of a single SimGroup at a time. /// /// The SimGroup will automatically enforce the single-group-membership rule. /// /// @code /// // From engine/sim/simPath.cc - getting a pointer to a SimGroup /// SimGroup* pMissionGroup = dynamic_cast(Sim::findObject("MissionGroup")); /// /// // From game/trigger.cc:46 - iterating over a SimObject's group. /// SimObject* trigger = ...; /// SimGroup* pGroup = trigger->getGroup(); /// for (SimGroup::iterator itr = pGroup->begin(); itr != pGroup->end(); itr++) /// { /// // do something with *itr /// } /// @endcode class SimGroup: public SimSet { public: typedef SimSet Parent; friend class SimManager; friend class SimObject; private: SimNameDictionary mNameDictionary; void _addObject( SimObject* object, bool forcePushBack = false ); void _removeObjectNoLock( SimObject* ); public: ~SimGroup(); void addObject( SimObject* object, SimObjectId id); void addObject( SimObject* object, const char* name ); // SimSet. virtual void addObject( SimObject* object ); virtual void removeObject( SimObject* object ); virtual void pushObject( SimObject* object ); virtual void popObject(); virtual void clear(); virtual SimGroup* clone(); virtual SimGroup* deepClone(); virtual SimObject* findObject(const char* name); virtual void onRemove(); virtual bool processArguments( S32 argc, ConsoleValue *argv ); virtual SimObject* getObject(const S32& index); DECLARE_CONOBJECT( SimGroup ); }; inline void SimGroup::addObject(SimObject* obj, SimObjectId id) { obj->mId = id; dSprintf( obj->mIdString, sizeof( obj->mIdString ), "%u", obj->mId ); addObject( obj ); } inline void SimGroup::addObject(SimObject *obj, const char *name) { addObject( obj ); obj->assignName(name); } /// An iterator that recursively and exhaustively traverses all objects /// in an SimGroup object tree. class SimGroupIterator: public SimSetIterator { public: SimGroupIterator(SimGroup* grp): SimSetIterator(grp) {} SimObject* operator++(); }; #endif // _SIMSET_H_