00001 /* 00002 * PROPRIETARY INFORMATION. This software is proprietary to 00003 * Side Effects Software Inc., and is not to be reproduced, 00004 * transmitted, or disclosed in any way without written permission. 00005 * 00006 * Produced by: 00007 * Side Effects Software Inc. 00008 * 477 Richmond Street West, Suite 1001 00009 * Toronto, Ontario 00010 * Canada M5V 3E7 00011 * 416-504-9876 00012 */ 00013 00014 #ifndef __SIM_Engine_h__ 00015 #define __SIM_Engine_h__ 00016 00017 #include "SIM_API.h" 00018 #include <UT/UT_HashTable.h> 00019 #include <UT/UT_SymbolTable.h> 00020 #include <OP/OP_Value.h> 00021 #include "SIM_Error.h" 00022 #include "SIM_Cache.h" 00023 #include "SIM_Data.h" 00024 00025 #define SIM_PATH_SEPCHAR '/' 00026 #define SIM_OBJECT_SEPCHAR ':' 00027 #define SIM_OBJECTNUM_SEPCHAR '.' 00028 #define SIM_OBJECTDATA_SEPCHAR '/' 00029 00030 class UT_InfoTree; 00031 class UT_StringArray; 00032 class OP_Node; 00033 class SIM_BaseCache; 00034 class SIM_DopDescription; 00035 class SIM_DataFactoryCreator; 00036 class SIM_DataFactory; 00037 class SIM_Data; 00038 class SIM_SimulationState; 00039 class SIM_MetaObject; 00040 class SIM_MetaObjectArray; 00041 class SIM_ConstObjectArray; 00042 class SIM_ObjectArray; 00043 class SIM_SaveCommand; 00044 00045 /// This is the main engine behind all simulations. Any number of these 00046 /// can be created to create independent simulations. Generally a simulation 00047 /// will be owned by a particular OP_Node. For example, the DopNet object 00048 /// contains a SIM_Engine. 00049 class SIM_API SIM_Engine 00050 { 00051 public: 00052 /// Constructor to create a new simulation. 00053 SIM_Engine(OP_Node *owner); 00054 /// Destructor to destroy all data associated with a simulation. 00055 virtual ~SIM_Engine(); 00056 00057 /// Returns the OP_Node that owns this engine. This may be null if 00058 /// the engine isn't associated with a particular node. 00059 OP_Node *getOwner() const; 00060 /// Gets the full path to the DOP Network node that owns this simulation. 00061 const UT_String &getOwnerFullPath() const; 00062 00063 /// Allocates a new SIM_Object and adds it to the simulation. 00064 /// The name parameter is assigned to the object. This name does not 00065 /// have to be unique. 00066 SIM_Object *addSimulationObject(bool solvefirstframe); 00067 /// Removes a SIM_Object from the simulation. 00068 /// The index value is the current location of the object in the 00069 /// simulations's list of objects. This value will not be the same 00070 /// as the SIM_Object::getObjectId() value, and may change when objects 00071 /// are added to or removed from the simulation. 00072 void removeSimulationObject(int index); 00073 /// Removes a SIM_Object from the simulation. 00074 /// This version of this function takes a SIM_Object pointer instead 00075 /// of an index. The pointer is converted to an index and the indexed 00076 /// version of removeSimulationObject() is called. 00077 void removeSimulationObject(SIM_Object *object); 00078 /// Returns the number of objects currently in the simulation. 00079 int getNumSimulationObjects() const; 00080 /// Returns a pointer to the SIM_Object at the specified index. 00081 /// The index value is the current location of the object in the 00082 /// simulations's list of objects. This value will not be the same 00083 /// as the SIM_Object::getObjectId() value. 00084 const SIM_Object *getSimulationObject(int index) const; 00085 /// Returns a pointer to the SIM_Object with the specified unique id. 00086 /// This function searches the current list of objects for the 00087 /// specified objectid. If the objectid is not found, this function 00088 /// returns null. 00089 const SIM_Object *getSimulationObjectFromId(int objectid) const; 00090 /// Returns the "nth" simulation object that matches a supplied filter. 00091 /// If less than the supplied n objects match the filter, 0 is returned. 00092 const SIM_Object *getNthSimulationObject( 00093 const SIM_DataFilter &filter, 00094 int n) const; 00095 00096 /// Creates a new relationship. The creationflags parameter accepts the 00097 /// same SIM_DATA_* flags accepted by the creationflags parameter of 00098 /// SIM_Data::createNamedSubData(). 00099 SIM_Relationship *addRelationship(const char *name, 00100 int creationflags); 00101 /// Returns the number of relationships in the current simulation state. 00102 int getNumRelationships() const; 00103 /// This function allows looping through the simulations relationships. 00104 const SIM_Relationship *getRelationship(int index) const; 00105 /// Returns the relationship with the specified name. 00106 const SIM_Relationship *getRelationship(const char *name) const; 00107 /// Gets all the relationships of a particular type. The type of a 00108 /// relationship is defined by the one subdata on the SIM_Relationship. 00109 void filterConstRelationships( 00110 const SIM_DataFilter &filter, 00111 SIM_ConstDataArray &rels) const; 00112 /// Removes the relationship at the specified position. 00113 void removeRelationship(int index); 00114 /// Removes the relationship with the specified name. 00115 void removeRelationship(const char *name); 00116 /// Removes the specified relationship. 00117 void removeRelationship(SIM_Relationship *rel); 00118 /// Removes all relationships created by a particular DOP node. If -1 00119 /// is specified, then all relationships are removed. Relationships in 00120 /// the "except" array are not removed. 00121 void removeAllRelationships(int createdby, 00122 const SIM_RelationshipArray *except); 00123 00124 /// Returns either a SIM_Relationship or SIM_Object depending on the 00125 /// information stored in the id parameter. 00126 const SIM_RootData *getRootDataFromId( 00127 const SIM_RootDataId &id) const; 00128 00129 /// Returns a pointer to the state of an object at some time in the past. 00130 /// This function searches for these past object states in three ways. 00131 /// First, if the specified time is equal to a past time step, the state 00132 /// for that time step is returned. Second, if the specified time is not 00133 /// equal to a past time step, then past time steps are searched for 00134 /// object states stored explicitly with createSubStepObject(). Finally, 00135 /// if both these methods fail to find a matching object state, then the 00136 /// closest object states before and after the specified time are found 00137 /// using the first two approaches. Then a new object state is created by 00138 /// interpolating between these two states using SIM_Data::interpolate(). 00139 /// This third approach is skipped if allowinterpolation is set to false. 00140 /// If no previous or no future object state can be found, then this 00141 /// function returns null. 00142 const SIM_Object *getObjectAtTime(const SIM_Object &object, 00143 const SIM_Time &time, 00144 bool allowinterpolation) const; 00145 /// Returns a non-const pointer to an object for a particular time. 00146 /// This function is very similar to getObjectAtTime() except for two 00147 /// things. First, it requires a non-const starting object, and it is 00148 /// a non-const function. Second, if the specified time exactly matches 00149 /// a time in the past, the returned object will be a copy of that past 00150 /// object, not the object itself. Otherwise modifying that object could 00151 /// modify the past. However, if the current time is sepcified, the actual 00152 /// object (i.e. a pointer to the passed in SIM_Object) is returned. 00153 /// This function is for use by solvers that want to modify their 00154 /// affector objects and which do substepping. 00155 SIM_Object *getAffectorAtTime(SIM_Object &object, 00156 const SIM_Time &time, 00157 bool allowinterpolation); 00158 /// Creates an object in the current state with a particular time stamp. 00159 /// This function is useful for solvers that need to do their own time 00160 /// stepping with step sizes smaller than the globally specified time 00161 /// step size. These explicitly stored subsamples are accessible later 00162 /// with the getObjectAtTime() function. 00163 SIM_Object *createSubStepObject(const SIM_Object &object, 00164 const SIM_Time &time); 00165 /// Removes an object previously created by createSubStepObject(). 00166 /// If the allwithmatchingid parameter is set to true, not only will 00167 /// the specified object be destroyed, but all other objects with the 00168 /// same object id created using createSubStepObject() will be destroyed. 00169 /// This provides a quick way to destroy all temporary working copies 00170 /// of a particular object. 00171 void removeSubStepObject(SIM_Object *objectattime, 00172 bool allwithmatchingid); 00173 00174 /// Loads a simulation state from a file and merges it into the current 00175 /// simulation state. The data that gets loaded is not allowed to do any 00176 /// sharing of data with the current state, since the data id's can't 00177 /// be expected to match. Objects from the file will replace objects from 00178 /// the current state if their object ids match. Objects loaded from the 00179 /// file will be flagged so that they will not be solved in the current 00180 /// timestep. In subsequent timesteps these objects will be solved. 00181 /// The filter parameter specifies which objects stored in the file 00182 /// are merged in. 00183 bool mergeSimulationFile(const char *filename, 00184 const SIM_DataFilter &filter, 00185 const SIM_ObjectArray &replaceobjects, 00186 const OP_Node *defaultcreator, 00187 bool forcecreatortodefault, 00188 bool acceptnewobjects, 00189 bool matchbyname, 00190 const char *nameprefix); 00191 /// Schedules some objects to be saved to a files at the end of the 00192 /// current timestep. The filter parameter makes it possible to save 00193 /// only some of the objects to the file. The actual save happens 00194 /// in the postSimulationStep(). 00195 void saveSimulationFile(const char *filename, 00196 const SIM_DataFilter &filter, 00197 const SIM_ObjectArray &objects, 00198 bool docompress = true) const; 00199 /// Saves the entire current state of the simulation immediately. 00200 void saveSimulationState(ostream &os, 00201 bool docompress = true) const; 00202 00203 /// Sets the current time for the simulation. If the new simulation 00204 /// time is less than the current time, the SIM_Cache is simply set to 00205 /// use its best match for the chosen time. If the new simulation time 00206 /// is greater than the current time, the simulation is stepped forward 00207 /// in time to the new time. The resimlasttimestep parameter causes the 00208 /// most recent cache entry to be deleted before trying to set the 00209 /// current time. This is useful if some parameter of the simulation 00210 /// changes on the current timestep. It causes the timestep to be 00211 /// recooked with the new parameter. This resimulation may also happen 00212 /// if the simulation was interrupted the last time this function was 00213 /// called. The forceresetsim parameter tells the SIM_Engine to reset 00214 /// the simulation before trying to move to the specified time, whether 00215 /// the reset is required or not. 00216 void setSimulationTime(const SIM_Time &t, 00217 bool resimlasttimestep, 00218 bool forceresetsim, 00219 bool allowsimulation); 00220 /// Returns the current simulation time. 00221 const SIM_Time &getSimulationTime() const; 00222 /// Returns the simulation time that corresponds to the given global time. 00223 const SIM_Time getEngineTime(const SIM_Time &t) const; 00224 /// Returns the global time that corresponds to the given simulation time. 00225 const SIM_Time getGlobalTime(const SIM_Time &t) const; 00226 /// Returns the time of the earliest simulation state in the cache. 00227 const SIM_Time &getEarliestCacheTime() const; 00228 /// Returns a reference to the cache options for this simulation. 00229 const SIM_CacheOptions &getCacheOptions() const; 00230 /// Sets the cache options for this simulation. 00231 void setCacheOptions(const SIM_CacheOptions &o); 00232 /// Returns the time step size. 00233 const SIM_Time &getTimeStep() const; 00234 /// Sets the global time step size. 00235 void setTimeStep(const SIM_Time &time); 00236 /// Gets the maximum number of allowed feedback iterations. 00237 int getMaxFeedbackIterations() const; 00238 /// Sets the maximum number of allowed feedback iterations. 00239 void setMaxFeedbackIterations(int max); 00240 /// Gets the current feedback iteration we are on. If we are not 00241 /// currently solving, this function will return -1; 00242 int getFeedbackIteration() const; 00243 /// Returns true if the engine is providing data hierarchy hints. 00244 bool getProvideDataHints() const; 00245 /// Specifies whether the engine should provide data hierarchy hints. 00246 void setProvideDataHints(bool providehints); 00247 /// Gets the id for the node that is currently being processed. 00248 int getCurrentCreatorId() const; 00249 /// Gets the index of the node output that is currently being processed. 00250 int getCurrentCreatorIndex() const; 00251 /// Set the creator information. These values are automatically reset 00252 /// to (-1, -1) before performing a simulation step. 00253 void setCreatorInfo(int id, int index); 00254 /// Returns the SIM_Data pointer with the specified unique id. 00255 const SIM_Data *getConstDataWithId(const UT_Guid &id) const; 00256 /// Returns the simulation frame number that corresponds to the given 00257 /// time. The simulation frame number is the number of timesteps that 00258 /// need to be run to get to the specified time, plus one (so that 00259 /// time zero is frame 1). 00260 int getSimulationFrame(const SIM_Time &time) const; 00261 /// Returns the simulation time that corresponds to the given simulation 00262 /// frame. 00263 const SIM_Time getSimulationTime(int frame) const; 00264 00265 /// Adds an error to the engine. The object and data parameters allow 00266 /// the DOP_Engine to put the error message on an appropriate DOP Node. 00267 /// Errors are found in the SIM error file. This function simply calls 00268 /// addErrorSubclass(). 00269 void addError(const SIM_RootData *rootdata, 00270 const SIM_Data *data, 00271 int errorcode, 00272 const char *errorparm, 00273 UT_ErrorSeverity severity) const; 00274 00275 /// Returns the total amount of memory used by all data currently in 00276 /// this simulation, at the current time or in the cache. 00277 int64 getTotalMemorySize() const; 00278 00279 /// Removes all node interests from the engine. 00280 void removeAllOPInterests() const; 00281 /// Adds an interest in the supplied node. When the node changes, the 00282 /// engine's handleExternalNodeChange function will be called. 00283 void addOPInterest(OP_Node *node) const; 00284 00285 /// Returns all available data type names. 00286 /// If a null type pointer is passed in, this function returns a list 00287 /// of every data type that the SIM_Engine can create. If a data type 00288 /// name is passed in, the list returned will only contain those data 00289 /// types that can be cast to the specified data type using the 00290 /// SIM_Data::getPointerToType() function. 00291 void getDataTypes(UT_StringArray &datatypes, 00292 const char *datatype) const; 00293 /// Returns all available data types and the source of their definition. 00294 /// The definition source will be an empty string if the data type is 00295 /// defined internally to Houdini. Otherwise it will be the name of the 00296 /// DSO or DLL file that contains the data type. 00297 void getDataTypeSources(UT_StringArray &datatypes, 00298 UT_StringArray &srcs) const; 00299 /// Returns the descriptive name for a data type. 00300 /// Given the unique identifying name for a data type this function 00301 /// finds the appropriate data factory and returns the descriptive 00302 /// string assigned to the type. 00303 const char *getDescription(const char *datatype) const; 00304 /// Returns the SIM_DopDescription from the SIM_DataFactory of a 00305 /// particular data type. 00306 const SIM_DopDescription *getDopDescription(const char *datatype) const; 00307 00308 /// Filters all our simulation objects based on a string pattern. 00309 /// Note that the time value is a simulation time, not global time. 00310 void findAllObjectsFromString(const char *objspec, 00311 SIM_ConstObjectArray &objects, 00312 const SIM_Time &t, 00313 bool interpolateobj) const; 00314 /// Returns a single object matching a specified string pattern. 00315 /// Note that the time value is a simulation time, not global time. 00316 const SIM_Object *findObjectFromString(const char *objspec, 00317 int whichmatch, int *nummatch, 00318 const SIM_Time &t, 00319 bool interpolateobj) const; 00320 /// Returns a list of objects created by the specified node, or any 00321 /// nodes inside the specified node (if it is a subnet). 00322 void findAllObjectsCreatedBy(OP_Node *creator, 00323 SIM_ConstObjectArray &objects) const; 00324 /// Filters all our relationships based on a string pattern. 00325 /// Note that the time value is a simulation time, not global time. 00326 void findAllRelationshipsFromString( 00327 const char *relspec, 00328 SIM_ConstDataArray &relationships, 00329 const SIM_Time &t, 00330 bool interpolaterel) const; 00331 /// Returns a single relationship matching a specified string pattern. 00332 /// Note that the time value is a simulation time, not global time. 00333 const SIM_Relationship *findRelationshipFromString( 00334 const char *relspec, 00335 int whichmatch, int *nummatch, 00336 const SIM_Time &t, 00337 bool interpolaterel) const; 00338 00339 /// Gets a list of all selected data in the simulation. 00340 void getSelection(SIM_ConstDataArray &data) const; 00341 /// Clears the selection flag on all data in this simulation. 00342 void clearSelection() const; 00343 00344 /// Returns the scale factor to convert a value in the provided units 00345 /// into the equivalent MKS value. 00346 fpreal scaleToMKS(const char *fromunits) const; 00347 /// Returns the scale factor to convert a value in MKS units into the 00348 /// equivalent value in the provided units. 00349 fpreal scaleFromMKS(const char *tounits) const; 00350 00351 protected: 00352 /// Resets the simulation to a clean state. This function calls 00353 /// clearSimulation(), then builds a single empty simulation state. 00354 virtual void resetSimulation(); 00355 /// This function is run before passing each object to its solver. 00356 /// The implementation at this level clears out all temporary data 00357 /// from the simulation objects. 00358 virtual void preSimulationStep(); 00359 /// This function is run after all objects are processed by their solvers. 00360 virtual void postSimulationStep(); 00361 /// This function is called by our cache whenever the most recent 00362 /// simulated timestep is being removed from memory (either deleted 00363 /// or moved onto disk). 00364 virtual void clearReferencesToMostRecentState(); 00365 /// Initializes a newly created object. This function is called by 00366 /// addSimulationObject(). The default implementation does nothing. 00367 virtual void objectCreated(SIM_Object *object); 00368 /// Allows special handling of objects that are being removed. This 00369 /// function is called by removeSimulationObject(). The default 00370 /// implementation does nothing. 00371 virtual void objectRemoved(SIM_Object *object); 00372 /// Override this method to handle changes to external nodes in which 00373 /// we have an interest. The default implementation does nothing. 00374 virtual void handleExternalNodeChangeSubclass( 00375 OP_Node *changednode, 00376 OP_EventType eventtype, 00377 void *data); 00378 /// The default implementation of this function does nothing. 00379 virtual void addErrorSubclass(const SIM_RootData *rootdata, 00380 const SIM_Data *data, 00381 int errorcode, 00382 const char *errorparm, 00383 UT_ErrorSeverity severity) const; 00384 /// Returns the simulation time that corresponds to the given global time. 00385 virtual const SIM_Time getEngineTimeSubclass(const SIM_Time &t) const; 00386 /// Returns the global time that corresponds to the given simulation time. 00387 virtual const SIM_Time getGlobalTimeSubclass(const SIM_Time &t) const; 00388 /// This function is the only way for a SIM_Engine subclass to get a 00389 /// non-const SIM_Object pointer. 00390 SIM_Object *getNonConstSimulationObject(int index); 00391 00392 private: 00393 /// Completely clears all existing data related to a simulation. 00394 void clearSimulation(); 00395 /// Takes one time step forward in the simulation. 00396 void doSimulationStep(bool forreset); 00397 /// Solves a meta object for one time step. All affectors will already 00398 /// be solved. 00399 void doSimulationStepForMetaObject( 00400 SIM_MetaObject &metaobject, 00401 const SIM_Time ×tep); 00402 /// Merges another simulation state in with our current state. This 00403 /// state can come from a file or another live simulation. 00404 void mergeSimulation( 00405 SIM_SimulationState &fromstate, 00406 const SIM_DataFilter *filter, 00407 const SIM_ObjectArray *replaceobjects, 00408 bool acceptnewobjects, 00409 bool matchbyname, 00410 const char *nameprefix); 00411 00412 /// Gets the current simulation state. Notably, it returns non-const. 00413 /// It is quite questionable to use this non-const function in 00414 /// solvers! One should acquire non-const SIM_Objects through 00415 /// your affector lists or other ways. 00416 SIM_SimulationState &getSimulationObjects(); 00417 /// Gets a const reference to the current simulation state. 00418 const SIM_SimulationState &getSimulationObjects() const; 00419 00420 /// Returns the SIM_Data pointer with the specified unique id. 00421 SIM_Data *getDataWithId(const UT_Guid &id) const; 00422 /// Destroy any data that has a zero reference count. 00423 void purgeUnusedData() const; 00424 /// This is the function traversed by the myAllData hash table from 00425 /// purgeUnusedData(). It adds any unused data to an array for later 00426 /// deletion. 00427 static int deleteUnusedData(UT_Thing &thing, 00428 const UT_Hash &hash, void *p); 00429 /// This is the functions traversed by myAllData from getTotalMemorySize(). 00430 /// It adds the memory size of each SIM_Data to a total size value passed 00431 /// through the p parameter. 00432 static int getDataMemorySize(UT_Thing &thing, 00433 const UT_Hash &hash, void *p); 00434 /// This is the function traversed by the myAllData hash table from 00435 /// getSelection(). It fills an array with all selected SIM_Data. 00436 static int getSelectedData(UT_Thing &thing, 00437 const UT_Hash &hash, void *p); 00438 /// This is the function traversed by the myAllData hash table from 00439 /// clearSelection(). It clears the selection flag on each SIM_Data. 00440 static int deselectData(UT_Thing &thing, 00441 const UT_Hash &hash, void *p); 00442 00443 /// Register a new piece of data with the engine. This is 00444 /// called from the SIM_DataFactory newData function to add 00445 /// the data to myAllData and myAllChangedSizeData. 00446 void registerNewData(SIM_Data *newdata) const; 00447 /// Create data using our factories. 00448 /// Only SIM_Data should use this functions. 00449 SIM_Data *newData(const char *datatype) const; 00450 /// Create data using our factories, and set the uniqueid of the new data. 00451 /// Only SIM_SimulationState should use this functions. 00452 SIM_Data *newData(const char *datatype, 00453 const UT_Guid &uniqueid) const; 00454 /// Unregister a piece of data that is being deleted. This is 00455 /// called from the SIM_DataFactory deleteData function to remove 00456 /// the data from myAllData and myAllChangedSizeData. 00457 void unregisterDeletedData(SIM_Data *deleteddata) const; 00458 /// Delete data using our factories. 00459 /// Only SIM_Data should use this functions. 00460 void deleteData(SIM_Data *data) const; 00461 /// This function is called when the unique id of a SIM_Data changes. 00462 /// It lets us keep the myAllData hash table up to date. 00463 void changeDataUniqueId(const SIM_Data *data, 00464 const UT_Guid &newid) const; 00465 /// This function is called when a piece of data has decided it needs 00466 /// to recalculate its memory size. 00467 void setNeedsToRecalculateMemorySize( 00468 const SIM_Data *data) const; 00469 00470 /// Access our data factories. 00471 SIM_DataFactory *getDataFactory(const char *name) const; 00472 void addDataFactory(SIM_DataFactory *factory); 00473 00474 /// This function is called when an external node in which we have an 00475 /// interest changes. It calls handleExternalNodeChangeSubclass. 00476 void handleExternalNodeChange(OP_Node *changednode, 00477 OP_EventType eventtype, 00478 void *data); 00479 /// This static version of handleExternalNodeChange converts the callee 00480 /// pointer to a SIM_Engine pointer and calls handleExternalNodeChange 00481 /// on that engine. 00482 static void handleExternalNodeChange(OP_Node *changednode, 00483 void *callee, 00484 OP_EventType eventtype, 00485 void *data); 00486 00487 /// Adds a SIM_DataFactoryCreator to the global list. 00488 static void addDataFactoryCreator(SIM_DataFactoryCreator *creator); 00489 /// Removes a SIM_DataFactoryCreator from the global list. 00490 static void removeDataFactoryCreator(SIM_DataFactoryCreator *creator); 00491 /// Returns the global list of data factory creators. 00492 static UT_PtrArray<SIM_DataFactoryCreator *> &getCreators(); 00493 00494 UT_SymbolTable myFactories; 00495 SIM_Cache myCache; 00496 SIM_BaseCache *myBaseCache; 00497 SIM_Time myTimeStep; 00498 OP_Node *myOwner; 00499 int myMaxFeedbackIterations; 00500 int myFeedbackIteration; 00501 int myCurrentCreatorId; 00502 int myCurrentCreatorIndex; 00503 mutable UT_String myOwnerFullPath; 00504 mutable UT_PtrArray<SIM_SaveCommand *> mySaveCommands; 00505 mutable UT_HashTable myAllData; 00506 mutable UT_HashTable myAllChangedSizeData; 00507 mutable UT_PtrArraySorted<OP_Node *> myOPInterestNodes; 00508 mutable int64 myTotalMemorySize; 00509 bool myProvideDataHints; 00510 bool myFirstSimulationStep; 00511 bool myLastCookInterrupted; 00512 00513 static UT_SymbolTable theDataTypeRequests; 00514 00515 friend class SIM_Data; 00516 friend class SIM_DataFactory; 00517 friend class SIM_DataFactoryCreator; 00518 friend class SIM_SimulationState; 00519 friend class SIM_SaveCommand; 00520 friend class SIM_BaseCache; 00521 friend class SIM_Cache; 00522 }; 00523 00524 #endif 00525
1.5.9