simObject.cc 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "sim/simObject.h"
  23. #include "sim/simObjectTimerEvent.h"
  24. #include "console/consoleInternal.h"
  25. #include "console/codeBlock.h"
  26. #include "console/consoleInternal.h"
  27. #include "memory/frameAllocator.h"
  28. #include "io/fileStream.h"
  29. #include "io/fileObject.h"
  30. #include "console/consoleTypeValidators.h"
  31. //-----------------------------------------------------------------------------
  32. IMPLEMENT_CONOBJECT(SimObject);
  33. namespace Sim
  34. {
  35. extern U32 gNextObjectId;
  36. extern SimIdDictionary *gIdDictionary;
  37. extern SimManagerNameDictionary *gNameDictionary;
  38. extern void cancelPendingEvents(SimObject *obj);
  39. }
  40. //-----------------------------------------------------------------------------
  41. SimObject::SimObject( const U8 namespaceLinkMask ) : mNSLinkMask( namespaceLinkMask )
  42. {
  43. mFlags.set( ModStaticFields | ModDynamicFields );
  44. objectName = NULL;
  45. mInternalName = NULL;
  46. nextNameObject = (SimObject*)-1;
  47. nextManagerNameObject = (SimObject*)-1;
  48. nextIdObject = NULL;
  49. mId = 0;
  50. mIdString = StringTable->EmptyString;
  51. mGroup = 0;
  52. mNameSpace = NULL;
  53. mNotifyList = NULL;
  54. mTypeMask = 0;
  55. mScriptCallbackGuard = 0;
  56. mFieldDictionary = NULL;
  57. mCanSaveFieldDictionary = true;
  58. mClassName = NULL;
  59. mSuperClassName = NULL;
  60. mProgenitorFile = CodeBlock::getCurrentCodeBlockFullPath();
  61. mPeriodicTimerID = 0;
  62. }
  63. //---------------------------------------------------------------------------
  64. bool SimObject::registerObject()
  65. {
  66. AssertFatal( !mFlags.test( Added ), "reigsterObject - Object already registered!");
  67. mFlags.clear(Deleted | Removed);
  68. if( mId == 0 )
  69. {
  70. mId = Sim::gNextObjectId++;
  71. char idBuffer[64];
  72. dSprintf(idBuffer, sizeof(idBuffer), "%d", mId);
  73. mIdString = StringTable->insert( idBuffer );
  74. }
  75. AssertFatal(Sim::gIdDictionary && Sim::gNameDictionary,
  76. "SimObject::registerObject - tried to register an object before Sim::init()!");
  77. Sim::gIdDictionary->insert(this);
  78. Sim::gNameDictionary->insert(this);
  79. // Notify object
  80. bool ret = onAdd();
  81. if(!ret)
  82. unregisterObject();
  83. AssertFatal(!ret || isProperlyAdded(), "Object did not call SimObject::onAdd()");
  84. if ( isMethod( "onAdd" ) )
  85. Con::executef( this, 1, "onAdd" );
  86. return ret;
  87. }
  88. //---------------------------------------------------------------------------
  89. void SimObject::unregisterObject()
  90. {
  91. // Sanity!
  92. AssertISV( getScriptCallbackGuard() == 0, "SimObject::unregisterObject: Object is being unregistered whilst performing a script callback!" );
  93. if ( isMethod( "onRemove" ) )
  94. Con::executef( this, 1, "onRemove" );
  95. mFlags.set(Removed);
  96. // Notify object first
  97. onRemove();
  98. // Clear out any pending notifications before
  99. // we call our own, just in case they delete
  100. // something that we have referenced.
  101. clearAllNotifications();
  102. // Notify all objects that are waiting for delete
  103. // messages
  104. if (getGroup())
  105. getGroup()->removeObject(this);
  106. processDeleteNotifies();
  107. // Do removals from the Sim.
  108. Sim::gNameDictionary->remove(this);
  109. Sim::gIdDictionary->remove(this);
  110. Sim::cancelPendingEvents(this);
  111. }
  112. //---------------------------------------------------------------------------
  113. void SimObject::deleteObject()
  114. {
  115. // Sanity!
  116. AssertISV( getScriptCallbackGuard() == 0, "SimObject::deleteObject: Object is being deleted whilst performing a script callback!" );
  117. AssertFatal(mFlags.test(Added),
  118. "SimObject::deleteObject: Object not registered.");
  119. AssertFatal(!isDeleted(),"SimManager::deleteObject: "
  120. "Object has already been deleted");
  121. AssertFatal(!isRemoved(),"SimManager::deleteObject: "
  122. "Object in the process of being removed");
  123. mFlags.set(Deleted);
  124. unregisterObject();
  125. delete this;
  126. }
  127. //---------------------------------------------------------------------------
  128. void SimObject::setId(SimObjectId newId)
  129. {
  130. if(!mFlags.test(Added))
  131. {
  132. mId = newId;
  133. }
  134. else
  135. {
  136. // get this object out of the id dictionary if it's in it
  137. Sim::gIdDictionary->remove(this);
  138. // Free current Id.
  139. // Assign new one.
  140. mId = newId ? newId : Sim::gNextObjectId++;
  141. Sim::gIdDictionary->insert(this);
  142. }
  143. char idBuffer[64];
  144. dSprintf(idBuffer, sizeof(idBuffer), "%d", mId);
  145. mIdString = StringTable->insert( idBuffer );
  146. }
  147. void SimObject::assignName(const char *name)
  148. {
  149. // Added this assert 3/30/2007 because it is dumb to try to name
  150. // a SimObject the same thing as it's class name -patw
  151. //AssertFatal( dStricmp( getClassName(), name ), "Attempted to assign a name to a SimObject which matches it's type name." );
  152. if( dStricmp( getClassName(), name ) == 0 )
  153. Con::errorf( "SimObject::assignName - Assigning name '%s' to instance of object with type '%s'."
  154. " This can cause namespace linking issues.", getClassName(), name );
  155. #if 0
  156. Con::printf( "SimObject::assignName(%s)", name );
  157. #endif
  158. // Is this name already registered?
  159. if ( Sim::gNameDictionary->find(name) != NULL )
  160. {
  161. // Yes, so error,
  162. Con::errorf( "SimObject::assignName() - Attempted to set object to name '%s' but it is already assigned to another object.", name );
  163. return;
  164. }
  165. StringTableEntry newName = NULL;
  166. if(name[0])
  167. newName = StringTable->insert(name);
  168. if(mGroup)
  169. mGroup->nameDictionary.remove(this);
  170. if(mFlags.test(Added))
  171. Sim::gNameDictionary->remove(this);
  172. objectName = newName;
  173. if(mGroup)
  174. mGroup->nameDictionary.insert(this);
  175. if(mFlags.test(Added))
  176. Sim::gNameDictionary->insert(this);
  177. }
  178. //---------------------------------------------------------------------------
  179. bool SimObject::registerObject(U32 id)
  180. {
  181. setId(id);
  182. return registerObject();
  183. }
  184. bool SimObject::registerObject(const char *name)
  185. {
  186. assignName(name);
  187. return registerObject();
  188. }
  189. bool SimObject::registerObject(const char *name, U32 id)
  190. {
  191. setId(id);
  192. assignName(name);
  193. return registerObject();
  194. }
  195. void SimObject::assignDynamicFieldsFrom(SimObject* parent)
  196. {
  197. if(parent->mFieldDictionary)
  198. {
  199. if( mFieldDictionary == NULL )
  200. mFieldDictionary = new SimFieldDictionary;
  201. mFieldDictionary->assignFrom(parent->mFieldDictionary);
  202. }
  203. }
  204. void SimObject::assignFieldsFrom(SimObject *parent)
  205. {
  206. // only allow field assigns from objects of the same class:
  207. if(getClassRep() == parent->getClassRep())
  208. {
  209. const AbstractClassRep::FieldList &list = getFieldList();
  210. // copy out all the fields:
  211. for(U32 i = 0; i < (U32)list.size(); i++)
  212. {
  213. const AbstractClassRep::Field* f = &list[i];
  214. S32 lastField = f->elementCount - 1;
  215. for(S32 j = 0; j <= lastField; j++)
  216. {
  217. const char* fieldVal = (*f->getDataFn)( this, Con::getData(f->type, (void *) (((const char *)parent) + f->offset), j, f->table, f->flag));
  218. //if(fieldVal)
  219. // Con::setData(f->type, (void *) (((const char *)this) + f->offset), j, 1, &fieldVal, f->table);
  220. if(fieldVal)
  221. {
  222. // code copied from SimObject::setDataField().
  223. // TODO: paxorr: abstract this into a better setData / getData that considers prot fields.
  224. FrameTemp<char> buffer(2048);
  225. FrameTemp<char> bufferSecure(2048); // This buffer is used to make a copy of the data
  226. ConsoleBaseType *cbt = ConsoleBaseType::getType( f->type );
  227. const char* szBuffer = cbt->prepData( fieldVal, buffer, 2048 );
  228. dMemset( bufferSecure, 0, 2048 );
  229. dMemcpy( bufferSecure, szBuffer, dStrlen( szBuffer ) );
  230. if((*f->setDataFn)( this, bufferSecure ) )
  231. Con::setData(f->type, (void *) (((const char *)this) + f->offset), j, 1, &fieldVal, f->table);
  232. }
  233. }
  234. }
  235. }
  236. assignDynamicFieldsFrom(parent);
  237. }
  238. bool SimObject::writeField(StringTableEntry fieldname, const char* value)
  239. {
  240. // Don't write empty fields.
  241. if (!value || !*value)
  242. return false;
  243. // Don't write ParentGroup
  244. if( fieldname == StringTable->insert("parentGroup") )
  245. return false;
  246. return true;
  247. }
  248. void SimObject::writeFields(Stream &stream, U32 tabStop)
  249. {
  250. const AbstractClassRep::FieldList &list = getFieldList();
  251. for(U32 i = 0; i < (U32)list.size(); i++)
  252. {
  253. const AbstractClassRep::Field* f = &list[i];
  254. if( f->type == AbstractClassRep::DepricatedFieldType ||
  255. f->type == AbstractClassRep::StartGroupFieldType ||
  256. f->type == AbstractClassRep::EndGroupFieldType) continue;
  257. // Fetch fieldname.
  258. StringTableEntry fieldName = StringTable->insert( f->pFieldname );
  259. // Fetch element count.
  260. const S32 elementCount = f->elementCount;
  261. // Skip if the field should not be written.
  262. // For now, we only deal with non-array fields.
  263. if ( elementCount == 1 &&
  264. f->writeDataFn != NULL &&
  265. f->writeDataFn( this, fieldName ) == false )
  266. continue;
  267. for(U32 j = 0; S32(j) < elementCount; j++)
  268. {
  269. char array[8];
  270. dSprintf( array, 8, "%d", j );
  271. const char *val = getDataField(fieldName, array );
  272. // Make a copy for the field check.
  273. if (!val)
  274. continue;
  275. U32 nBufferSize = dStrlen( val ) + 1;
  276. FrameTemp<char> valCopy( nBufferSize );
  277. dStrcpy( (char *)valCopy, val );
  278. if (!writeField(fieldName, valCopy))
  279. continue;
  280. val = valCopy;
  281. U32 expandedBufferSize = ( nBufferSize * 2 ) + 32;
  282. FrameTemp<char> expandedBuffer( expandedBufferSize );
  283. if(f->elementCount == 1)
  284. dSprintf(expandedBuffer, expandedBufferSize, "%s = \"", f->pFieldname);
  285. else
  286. dSprintf(expandedBuffer, expandedBufferSize, "%s[%d] = \"", f->pFieldname, j);
  287. // detect and collapse relative path information
  288. char fnBuf[1024];
  289. if (f->type == TypeFilename)
  290. {
  291. Con::collapsePath(fnBuf, 1024, val);
  292. val = fnBuf;
  293. }
  294. expandEscape((char*)expandedBuffer + dStrlen(expandedBuffer), val);
  295. dStrcat(expandedBuffer, "\";\r\n");
  296. stream.writeTabs(tabStop);
  297. stream.write(dStrlen(expandedBuffer),expandedBuffer);
  298. }
  299. }
  300. if(mFieldDictionary && mCanSaveFieldDictionary)
  301. mFieldDictionary->writeFields(this, stream, tabStop);
  302. }
  303. void SimObject::write(Stream &stream, U32 tabStop, U32 flags)
  304. {
  305. // Only output selected objects if they want that.
  306. if((flags & SelectedOnly) && !isSelected())
  307. return;
  308. stream.writeTabs(tabStop);
  309. char buffer[1024];
  310. dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() ? getName() : "");
  311. stream.write(dStrlen(buffer), buffer);
  312. writeFields(stream, tabStop + 1);
  313. stream.writeTabs(tabStop);
  314. stream.write(4, "};\r\n");
  315. }
  316. bool SimObject::save(const char* pcFileName, bool bOnlySelected)
  317. {
  318. static const char *beginMessage = "//--- OBJECT WRITE BEGIN ---";
  319. static const char *endMessage = "//--- OBJECT WRITE END ---";
  320. FileStream stream;
  321. FileObject f;
  322. f.readMemory(pcFileName);
  323. // check for flags <selected, ...>
  324. U32 writeFlags = 0;
  325. if(bOnlySelected)
  326. writeFlags |= SimObject::SelectedOnly;
  327. if(!ResourceManager->openFileForWrite(stream, pcFileName))
  328. return false;
  329. char docRoot[256];
  330. char modRoot[256];
  331. dStrcpy(docRoot, pcFileName);
  332. char *p = dStrrchr(docRoot, '/');
  333. if (p) *++p = '\0';
  334. else docRoot[0] = '\0';
  335. dStrcpy(modRoot, pcFileName);
  336. p = dStrchr(modRoot, '/');
  337. if (p) *++p = '\0';
  338. else modRoot[0] = '\0';
  339. Con::setVariable("$DocRoot", docRoot);
  340. Con::setVariable("$ModRoot", modRoot);
  341. const char *buffer;
  342. while(!f.isEOF())
  343. {
  344. buffer = (const char *) f.readLine();
  345. if(!dStrcmp(buffer, beginMessage))
  346. break;
  347. stream.write(dStrlen(buffer), buffer);
  348. stream.write(2, "\r\n");
  349. }
  350. stream.write(dStrlen(beginMessage), beginMessage);
  351. stream.write(2, "\r\n");
  352. write(stream, 0, writeFlags);
  353. stream.write(dStrlen(endMessage), endMessage);
  354. stream.write(2, "\r\n");
  355. while(!f.isEOF())
  356. {
  357. buffer = (const char *) f.readLine();
  358. if(!dStrcmp(buffer, endMessage))
  359. break;
  360. }
  361. while(!f.isEOF())
  362. {
  363. buffer = (const char *) f.readLine();
  364. stream.write(dStrlen(buffer), buffer);
  365. stream.write(2, "\r\n");
  366. }
  367. Con::setVariable("$DocRoot", NULL);
  368. Con::setVariable("$ModRoot", NULL);
  369. return true;
  370. }
  371. void SimObject::setInternalName(const char* newname)
  372. {
  373. if(newname)
  374. mInternalName = StringTable->insert(newname);
  375. }
  376. ConsoleMethod( SimObject, getInternalName, const char*, 2, 2, "getInternalName returns the objects internal name")
  377. {
  378. return object->getInternalName();
  379. }
  380. StringTableEntry SimObject::getInternalName()
  381. {
  382. return mInternalName;
  383. }
  384. ConsoleMethod(SimObject, dumpClassHierarchy, void, 2, 2, "obj.dumpClassHierarchy()")
  385. {
  386. object->dumpClassHierarchy();
  387. }
  388. void SimObject::dumpClassHierarchy()
  389. {
  390. AbstractClassRep* pRep = getClassRep();
  391. while(pRep)
  392. {
  393. Con::warnf("%s ->", pRep->getClassName());
  394. pRep = pRep->getParentClass();
  395. }
  396. }
  397. ConsoleMethod(SimObject, isMemberOfClass, bool, 3, 3, " isMemberOfClass(string classname) -- returns true if this object is a member of the specified class")
  398. {
  399. AbstractClassRep* pRep = object->getClassRep();
  400. while(pRep)
  401. {
  402. if(!dStricmp(pRep->getClassName(), argv[2]))
  403. {
  404. //matches
  405. return true;
  406. }
  407. pRep = pRep->getParentClass();
  408. }
  409. return false;
  410. }
  411. ConsoleMethod(SimObject, save, bool, 3, 4, "obj.save(fileName, [selectedOnly])")
  412. {
  413. bool bSelectedOnly = false;
  414. if(argc > 3)
  415. bSelectedOnly = dAtob(argv[3]);
  416. const char* filename = NULL;
  417. filename = argv[2];
  418. if(filename == NULL || *filename == 0)
  419. return false;
  420. return object->save(filename, bSelectedOnly);
  421. }
  422. ConsoleMethod(SimObject, setName, void, 3, 3, "obj.setName(newName)")
  423. {
  424. object->assignName(argv[2]);
  425. }
  426. ConsoleMethod(SimObject, getName, const char *, 2, 2, "obj.getName()")
  427. {
  428. const char *ret = object->getName();
  429. return ret ? ret : "";
  430. }
  431. ConsoleMethod(SimObject, getClassName, const char *, 2, 2, "obj.getClassName()")
  432. {
  433. const char *ret = object->getClassName();
  434. return ret ? ret : "";
  435. }
  436. ConsoleMethod(SimObject, getFieldValue, const char *, 3, 3, "obj.getFieldValue(fieldName);")
  437. {
  438. const char *fieldName = StringTable->insert( argv[2] );
  439. return object->getDataField( fieldName, NULL );
  440. }
  441. ConsoleMethod(SimObject, setFieldValue, bool, 4, 4, "obj.setFieldValue(fieldName,value);")
  442. {
  443. const char *fieldName = StringTable->insert(argv[2]);
  444. const char *value = argv[3];
  445. object->setDataField( fieldName, NULL, value );
  446. return true;
  447. }
  448. ConsoleMethod(SimObject, getFieldType, const char *, 3, 3, "obj.getFieldType(fieldName);")
  449. {
  450. const char *fieldName = StringTable->insert( argv[2] );
  451. U32 typeID = object->getDataFieldType( fieldName, NULL );
  452. ConsoleBaseType* type = ConsoleBaseType::getType( typeID );
  453. if( type )
  454. return type->getTypeClassName();
  455. return "";
  456. }
  457. ConsoleMethod( SimObject, call, const char*, 2, 0, "( %args ) - Dynamically call a method on an object." )
  458. {
  459. argv[1] = argv[2];
  460. return Con::execute( object, argc - 1, argv + 1 );
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Set the internal name, can be used to find child objects
  464. // in a meaningful way, usually from script, while keeping
  465. // common script functionality together using the controls "Name" field.
  466. //-----------------------------------------------------------------------------
  467. ConsoleMethod( SimObject, setInternalName, void, 3, 3, "string InternalName")
  468. {
  469. object->setInternalName(argv[2]);
  470. }
  471. ConsoleMethod(SimObject, getId, S32, 2, 2, "() Use the getId method to get the numeric ID of this shape.\n"
  472. "@return Returns the unique numeric ID of this shape.\n"
  473. "@sa getName, setName")
  474. {
  475. return object->getId();
  476. }
  477. ConsoleMethod(SimObject, getGroup, S32, 2, 2, "() Use the getGroup method to determine if this object is contained in a SimGroup and if so, which one.\n"
  478. "@return Returns the ID of the SimGroup this shape is in or zero if the shape is not contained in a SimGroup")
  479. {
  480. SimGroup *grp = object->getGroup();
  481. if(!grp)
  482. return -1;
  483. return grp->getId();
  484. }
  485. ConsoleMethod(SimObject, delete, void, 2, 2,"() Use the delete method to delete this object.\n"
  486. "When an object is deleted, it automatically: Unregisters its ID and name (if it has one) with the engine. Removes itself from any SimGroup or SimSet it may be a member of. (eventually) returns the memory associated with itself and its non-dynamic members. Cancels all pending %obj.schedule() events. For objects in the GameBase, ScriptObject, or GUIControl hierarchies, an object will first: Call the onRemove() method for the object's namespace\n"
  487. "@return No return value.")
  488. {
  489. object->deleteObject();
  490. }
  491. ConsoleMethod(SimObject,schedule, S32, 4, 0, "(time , command , <arg1 ... argN> ) Use the schedule method to schedule an action to be executed upon this object time milliseconds in the future.\n"
  492. "The major difference between this and the schedule console function is that if this object is deleted prior to the scheduled event, the event is automatically canceled. times should not be treated as exact since some 'simulation delay' is to be expected. The minimum resolution for a scheduled event is \"32 ms, or one tick. schedule does not validate the existence of command. i.e. If you pass an invalid console method name, the schedule() method will still return a schedule ID, but the subsequent event will fail silently.\n"
  493. "@param time Time in milliseconds till action is scheduled to occur.\n"
  494. "@param command Name of the command to execute. This command must be scoped to this object (i.e. It must exist in the namespace of the object), otherwise the schedule call will fail.\n"
  495. "@param arg1...argN These are optional arguments which will be passed to command. This version of schedule automatically passes the ID of %obj as arg0 to command.\n"
  496. "@return Returns an integer schedule ID.\n"
  497. "@sa See the schedule console function and its corresponding helper functions.")
  498. {
  499. U32 timeDelta = U32(dAtof(argv[2]));
  500. argv[2] = argv[3];
  501. argv[3] = argv[1];
  502. SimConsoleEvent *evt = new SimConsoleEvent(argc - 2, argv + 2, true);
  503. S32 ret = Sim::postEvent(object, evt, Sim::getCurrentTime() + timeDelta);
  504. // #ifdef DEBUG
  505. // Con::printf("obj %s schedule(%s) = %d", argv[3], argv[2], ret);
  506. // Con::executef(1, "backtrace");
  507. // #endif
  508. return ret;
  509. }
  510. static S32 QSORT_CALLBACK compareFields(const void* a,const void* b)
  511. {
  512. const AbstractClassRep::Field* fa = *((const AbstractClassRep::Field**)a);
  513. const AbstractClassRep::Field* fb = *((const AbstractClassRep::Field**)b);
  514. return dStricmp(fa->pFieldname, fb->pFieldname);
  515. }
  516. ConsoleMethod(SimObject, getDynamicFieldCount, S32, 2, 2, "obj.getDynamicFieldCount()")
  517. {
  518. S32 count = 0;
  519. SimFieldDictionary* fieldDictionary = object->getFieldDictionary();
  520. for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr)
  521. count++;
  522. return count;
  523. }
  524. ConsoleMethod(SimObject, getDynamicField, const char*, 3, 3, "obj.getDynamicField(index)")
  525. {
  526. SimFieldDictionary* fieldDictionary = object->getFieldDictionary();
  527. SimFieldDictionaryIterator itr(fieldDictionary);
  528. S32 index = dAtoi(argv[2]);
  529. for (S32 i = 0; i < index; i++)
  530. {
  531. if (!(*itr))
  532. {
  533. Con::warnf("Invalid dynamic field index passed to SimObject::getDynamicField!");
  534. return NULL;
  535. }
  536. ++itr;
  537. }
  538. char* buffer = Con::getReturnBuffer(256);
  539. if (*itr)
  540. {
  541. SimFieldDictionary::Entry* entry = *itr;
  542. dSprintf(buffer, 256, "%s", entry->slotName);
  543. return buffer;
  544. }
  545. Con::warnf("Invalid dynamic field index passed to SimObject::getDynamicField!");
  546. return NULL;
  547. }
  548. ConsoleMethod( SimObject, getFieldCount, S32, 2, 2, "() - Gets the number of persistent fields on the object." )
  549. {
  550. const AbstractClassRep::FieldList &list = object->getFieldList();
  551. const AbstractClassRep::Field* f;
  552. U32 numDummyEntries = 0;
  553. for(int i = 0; i < list.size(); i++)
  554. {
  555. f = &list[i];
  556. if( f->type == AbstractClassRep::DepricatedFieldType ||
  557. f->type == AbstractClassRep::StartGroupFieldType ||
  558. f->type == AbstractClassRep::EndGroupFieldType )
  559. {
  560. numDummyEntries++;
  561. }
  562. }
  563. return list.size() - numDummyEntries;
  564. }
  565. ConsoleMethod( SimObject, getField, const char*, 3, 3, "(int index) - Gets the name of the field at the given index." )
  566. {
  567. S32 index = dAtoi( argv[2] );
  568. const AbstractClassRep::FieldList &list = object->getFieldList();
  569. if( ( index < 0 ) || ( index >= list.size() ) )
  570. return "";
  571. const AbstractClassRep::Field* f;
  572. S32 currentField = 0;
  573. for(int i = 0; i < list.size() && currentField <= index; i++)
  574. {
  575. f = &list[i];
  576. // skip any dummy fields
  577. if(f->type == AbstractClassRep::DepricatedFieldType ||
  578. f->type == AbstractClassRep::StartGroupFieldType ||
  579. f->type == AbstractClassRep::EndGroupFieldType)
  580. {
  581. continue;
  582. }
  583. if(currentField == index)
  584. return f->pFieldname;
  585. currentField++;
  586. }
  587. // if we found nada, return nada.
  588. return "";
  589. }
  590. ConsoleMethod(SimObject,dump, void, 2, 2, "() Use the dump method to display the following information about this object: All engine and script registered console methods (including parent methods) for this object, all Non-Dynamic Fields AND all Dynamic Fields\n"
  591. "@return No return value")
  592. {
  593. const AbstractClassRep::FieldList &list = object->getFieldList();
  594. char expandedBuffer[4096];
  595. Con::printf("Member Fields:");
  596. Vector<const AbstractClassRep::Field *> flist(__FILE__, __LINE__);
  597. for(U32 i = 0; i < (U32)list.size(); i++)
  598. flist.push_back(&list[i]);
  599. dQsort(flist.address(),flist.size(),sizeof(AbstractClassRep::Field *),compareFields);
  600. for(Vector<const AbstractClassRep::Field *>::iterator itr = flist.begin(); itr != flist.end(); itr++)
  601. {
  602. const AbstractClassRep::Field* f = *itr;
  603. if( f->type == AbstractClassRep::DepricatedFieldType ||
  604. f->type == AbstractClassRep::StartGroupFieldType ||
  605. f->type == AbstractClassRep::EndGroupFieldType) continue;
  606. for(U32 j = 0; S32(j) < f->elementCount; j++)
  607. {
  608. // [neo, 07/05/2007 - #3000]
  609. // Some objects use dummy vars and projected fields so make sure we call the get functions
  610. //const char *val = Con::getData(f->type, (void *) (((const char *)object) + f->offset), j, f->table, f->flag);
  611. const char *val = (*f->getDataFn)( object, Con::getData(f->type, (void *) (((const char *)object) + f->offset), j, f->table, f->flag) );// + typeSizes[fld.type] * array1));
  612. if(!val /*|| !*val*/)
  613. continue;
  614. if(f->elementCount == 1)
  615. dSprintf(expandedBuffer, sizeof(expandedBuffer), " %s = \"", f->pFieldname);
  616. else
  617. dSprintf(expandedBuffer, sizeof(expandedBuffer), " %s[%d] = \"", f->pFieldname, j);
  618. expandEscape(expandedBuffer + dStrlen(expandedBuffer), val);
  619. Con::printf("%s\"", expandedBuffer);
  620. }
  621. }
  622. Con::printf("Tagged Fields:");
  623. if(object->getFieldDictionary())
  624. object->getFieldDictionary()->printFields(object);
  625. Con::printf("Methods:");
  626. Namespace *ns = object->getNamespace();
  627. Vector<Namespace::Entry *> vec(__FILE__, __LINE__);
  628. if(ns)
  629. ns->getEntryList(&vec);
  630. for(Vector<Namespace::Entry *>::iterator j = vec.begin(); j != vec.end(); j++)
  631. Con::printf(" %s() - %s", (*j)->mFunctionName, (*j)->mUsage ? (*j)->mUsage : "");
  632. }
  633. ConsoleMethod(SimObject, getType, S32, 2, 2, "() Use the getType method to get the type for this object. This type is an integer value composed of bitmasks. For simplicity, these bitmasks are defined in the engine and exposed for our use as global variables.\n"
  634. "To simplify the writing of scripts, a set of globals has been provided containing the bit setting for each class corresponding to a particular type.\n"
  635. "@return Returns a bit mask containing one or more set bits.\n"
  636. "@sa getClassName")
  637. {
  638. return((S32)object->getType());
  639. }
  640. bool SimObject::isMethod( const char* methodName )
  641. {
  642. if( !methodName || !methodName[0] )
  643. return false;
  644. StringTableEntry stname = StringTable->insert( methodName );
  645. if( getNamespace() )
  646. return ( getNamespace()->lookup( stname ) != NULL );
  647. return false;
  648. }
  649. ConsoleMethod(SimObject, isMethod, bool, 3, 3, "obj.isMethod(string method name)")
  650. {
  651. return object->isMethod( argv[2] );
  652. }
  653. const char *SimObject::tabComplete(const char *prevText, S32 baseLen, bool fForward)
  654. {
  655. return mNameSpace->tabComplete(prevText, baseLen, fForward);
  656. }
  657. //-----------------------------------------------------------------------------
  658. void SimObject::setDataField(StringTableEntry slotName, const char *array, const char *value)
  659. {
  660. // first search the static fields if enabled
  661. if(mFlags.test(ModStaticFields))
  662. {
  663. const AbstractClassRep::Field *fld = findField(slotName);
  664. if(fld)
  665. {
  666. if( fld->type == AbstractClassRep::DepricatedFieldType ||
  667. fld->type == AbstractClassRep::StartGroupFieldType ||
  668. fld->type == AbstractClassRep::EndGroupFieldType)
  669. return;
  670. S32 array1 = array ? dAtoi(array) : 0;
  671. if(array1 >= 0 && array1 < fld->elementCount && fld->elementCount >= 1)
  672. {
  673. // If the set data notify callback returns true, then go ahead and
  674. // set the data, otherwise, assume the set notify callback has either
  675. // already set the data, or has deemed that the data should not
  676. // be set at all.
  677. FrameTemp<char> buffer(2048);
  678. FrameTemp<char> bufferSecure(2048); // This buffer is used to make a copy of the data
  679. // so that if the prep functions or any other functions use the string stack, the data
  680. // is not corrupted.
  681. ConsoleBaseType *cbt = ConsoleBaseType::getType( fld->type );
  682. AssertFatal( cbt != NULL, "Could not resolve Type Id." );
  683. const char* szBuffer = cbt->prepData( value, buffer, 2048 );
  684. dMemset( bufferSecure, 0, 2048 );
  685. dMemcpy( bufferSecure, szBuffer, dStrlen( szBuffer ) );
  686. if( (*fld->setDataFn)( this, bufferSecure ) )
  687. Con::setData(fld->type, (void *) (((const char *)this) + fld->offset), array1, 1, &value, fld->table);
  688. onStaticModified( slotName, value );
  689. return;
  690. }
  691. if(fld->validator)
  692. fld->validator->validateType(this, (void *) (((const char *)this) + fld->offset));
  693. onStaticModified( slotName, value );
  694. return;
  695. }
  696. }
  697. if(mFlags.test(ModDynamicFields))
  698. {
  699. if(!mFieldDictionary)
  700. mFieldDictionary = new SimFieldDictionary;
  701. if(!array)
  702. mFieldDictionary->setFieldValue(slotName, value);
  703. else
  704. {
  705. char buf[256];
  706. dStrcpy(buf, slotName);
  707. dStrcat(buf, array);
  708. mFieldDictionary->setFieldValue(StringTable->insert(buf), value);
  709. }
  710. }
  711. }
  712. //-----------------------------------------------------------------------------
  713. const char *SimObject::getDataField(StringTableEntry slotName, const char *array)
  714. {
  715. if(mFlags.test(ModStaticFields))
  716. {
  717. S32 array1 = array ? dAtoi(array) : -1;
  718. const AbstractClassRep::Field *fld = findField(slotName);
  719. if(fld)
  720. {
  721. if(array1 == -1 && fld->elementCount == 1)
  722. return (*fld->getDataFn)( this, Con::getData(fld->type, (void *) (((const char *)this) + fld->offset), 0, fld->table, fld->flag) );
  723. if(array1 >= 0 && array1 < fld->elementCount)
  724. return (*fld->getDataFn)( this, Con::getData(fld->type, (void *) (((const char *)this) + fld->offset), array1, fld->table, fld->flag) );// + typeSizes[fld.type] * array1));
  725. return "";
  726. }
  727. }
  728. if(mFlags.test(ModDynamicFields))
  729. {
  730. if(!mFieldDictionary)
  731. return "";
  732. if(!array)
  733. {
  734. if (const char* val = mFieldDictionary->getFieldValue(slotName))
  735. return val;
  736. }
  737. else
  738. {
  739. static char buf[256];
  740. dStrcpy(buf, slotName);
  741. dStrcat(buf, array);
  742. if (const char* val = mFieldDictionary->getFieldValue(StringTable->insert(buf)))
  743. return val;
  744. }
  745. }
  746. return "";
  747. }
  748. //-----------------------------------------------------------------------------
  749. const char *SimObject::getPrefixedDataField(StringTableEntry fieldName, const char *array)
  750. {
  751. // Sanity!
  752. AssertFatal( fieldName != NULL, "Cannot get field value with NULL field name." );
  753. // Fetch field value.
  754. const char* pFieldValue = getDataField( fieldName, array );
  755. // Sanity.
  756. AssertFatal( pFieldValue != NULL, "Field value cannot be NULL." );
  757. // Return without the prefix if there's no value.
  758. if ( *pFieldValue == 0 )
  759. return StringTable->EmptyString;
  760. // Fetch the field prefix.
  761. StringTableEntry fieldPrefix = getDataFieldPrefix( fieldName );
  762. // Sanity!
  763. AssertFatal( fieldPrefix != NULL, "Field prefix cannot be NULL." );
  764. // Calculate a buffer size including prefix.
  765. const U32 valueBufferSize = dStrlen(fieldPrefix) + dStrlen(pFieldValue) + 1;
  766. // Fetch a buffer.
  767. char* pValueBuffer = Con::getReturnBuffer( valueBufferSize );
  768. // Format the value buffer.
  769. dSprintf( pValueBuffer, valueBufferSize, "%s%s", fieldPrefix, pFieldValue );
  770. return pValueBuffer;
  771. }
  772. //-----------------------------------------------------------------------------
  773. void SimObject::setPrefixedDataField(StringTableEntry fieldName, const char *array, const char *value)
  774. {
  775. // Sanity!
  776. AssertFatal( fieldName != NULL, "Cannot set object field value with NULL field name." );
  777. AssertFatal( value != NULL, "Field value cannot be NULL." );
  778. // Set value without prefix if there's no value.
  779. if ( *value == 0 )
  780. {
  781. setDataField( fieldName, NULL, value );
  782. return;
  783. }
  784. // Fetch the field prefix.
  785. StringTableEntry fieldPrefix = getDataFieldPrefix( fieldName );
  786. // Sanity.
  787. AssertFatal( fieldPrefix != NULL, "Field prefix cannot be NULL." );
  788. // Do we have a field prefix?
  789. if ( fieldPrefix == StringTable->EmptyString )
  790. {
  791. // No, so set the data field in the usual way.
  792. setDataField( fieldName, NULL, value );
  793. return;
  794. }
  795. // Yes, so fetch the length of the field prefix.
  796. const U32 fieldPrefixLength = dStrlen(fieldPrefix);
  797. // Yes, so does it start with the object field prefix?
  798. if ( dStrnicmp( value, fieldPrefix, fieldPrefixLength ) != 0 )
  799. {
  800. // No, so set the data field in the usual way.
  801. setDataField( fieldName, NULL, value );
  802. return;
  803. }
  804. // Yes, so set the data excluding the prefix.
  805. setDataField( fieldName, NULL, value + fieldPrefixLength );
  806. }
  807. //-----------------------------------------------------------------------------
  808. const char *SimObject::getPrefixedDynamicDataField(StringTableEntry fieldName, const char *array, const S32 fieldType )
  809. {
  810. // Sanity!
  811. AssertFatal( fieldName != NULL, "Cannot get field value with NULL field name." );
  812. // Fetch field value.
  813. const char* pFieldValue = getDataField( fieldName, array );
  814. // Sanity.
  815. AssertFatal( pFieldValue != NULL, "Field value cannot be NULL." );
  816. // Return the field if no field type is specified.
  817. if ( fieldType == -1 )
  818. return pFieldValue;
  819. // Return without the prefix if there's no value.
  820. if ( *pFieldValue == 0 )
  821. return StringTable->EmptyString;
  822. // Fetch the console base type.
  823. ConsoleBaseType* pConsoleBaseType = ConsoleBaseType::getType( fieldType );
  824. // Did we find the console base type?
  825. if ( pConsoleBaseType == NULL )
  826. {
  827. // No, so warn.
  828. Con::warnf("getPrefixedDynamicDataField() - Invalid field type '%d' specified for field '%s' with value '%s'.",
  829. fieldType, fieldName, pFieldValue );
  830. }
  831. // Fetch the field prefix.
  832. StringTableEntry fieldPrefix = pConsoleBaseType->getTypePrefix();
  833. // Sanity!
  834. AssertFatal( fieldPrefix != NULL, "Field prefix cannot be NULL." );
  835. // Calculate a buffer size including prefix.
  836. const U32 valueBufferSize = dStrlen(fieldPrefix) + dStrlen(pFieldValue) + 1;
  837. // Fetch a buffer.
  838. char* pValueBuffer = Con::getReturnBuffer( valueBufferSize );
  839. // Format the value buffer.
  840. dSprintf( pValueBuffer, valueBufferSize, "%s%s", fieldPrefix, pFieldValue );
  841. return pValueBuffer;
  842. }
  843. //-----------------------------------------------------------------------------
  844. void SimObject::setPrefixedDynamicDataField(StringTableEntry fieldName, const char *array, const char *value, const S32 fieldType )
  845. {
  846. // Sanity!
  847. AssertFatal( fieldName != NULL, "Cannot set object field value with NULL field name." );
  848. AssertFatal( value != NULL, "Field value cannot be NULL." );
  849. // Set value without prefix if no field type was specified.
  850. if ( fieldType == -1 )
  851. {
  852. setDataField( fieldName, NULL, value );
  853. return;
  854. }
  855. // Fetch the console base type.
  856. ConsoleBaseType* pConsoleBaseType = ConsoleBaseType::getType( fieldType );
  857. // Did we find the console base type?
  858. if ( pConsoleBaseType == NULL )
  859. {
  860. // No, so warn.
  861. Con::warnf("setPrefixedDynamicDataField() - Invalid field type '%d' specified for field '%s' with value '%s'.",
  862. fieldType, fieldName, value );
  863. }
  864. // Set value without prefix if there's no value or we didn't find the console base type.
  865. if ( *value == 0 || pConsoleBaseType == NULL )
  866. {
  867. setDataField( fieldName, NULL, value );
  868. return;
  869. }
  870. // Fetch the field prefix.
  871. StringTableEntry fieldPrefix = pConsoleBaseType->getTypePrefix();
  872. // Sanity.
  873. AssertFatal( fieldPrefix != NULL, "Field prefix cannot be NULL." );
  874. // Do we have a field prefix?
  875. if ( fieldPrefix == StringTable->EmptyString )
  876. {
  877. // No, so set the data field in the usual way.
  878. setDataField( fieldName, NULL, value );
  879. return;
  880. }
  881. // Yes, so fetch the length of the field prefix.
  882. const U32 fieldPrefixLength = dStrlen(fieldPrefix);
  883. // Yes, so does it start with the object field prefix?
  884. if ( dStrnicmp( value, fieldPrefix, fieldPrefixLength ) != 0 )
  885. {
  886. // No, so set the data field in the usual way.
  887. setDataField( fieldName, NULL, value );
  888. return;
  889. }
  890. // Yes, so set the data excluding the prefix.
  891. setDataField( fieldName, NULL, value + fieldPrefixLength );
  892. }
  893. //-----------------------------------------------------------------------------
  894. StringTableEntry SimObject::getDataFieldPrefix( StringTableEntry fieldName )
  895. {
  896. // Sanity!
  897. AssertFatal( fieldName != NULL, "Cannot get field prefix with NULL field name." );
  898. // Find the field.
  899. const AbstractClassRep::Field* pField = findField( fieldName );
  900. // Return nothing if field was not found.
  901. if ( pField == NULL )
  902. return StringTable->EmptyString;
  903. // Yes, so fetch the console base type.
  904. ConsoleBaseType* pConsoleBaseType = ConsoleBaseType::getType( pField->type );
  905. // Fetch the type prefix.
  906. return pConsoleBaseType->getTypePrefix();
  907. }
  908. //-----------------------------------------------------------------------------
  909. U32 SimObject::getDataFieldType( StringTableEntry slotName, const char* array )
  910. {
  911. const AbstractClassRep::Field* field = findField( slotName );
  912. if( field )
  913. return field->type;
  914. return 0;
  915. }
  916. SimObject::~SimObject()
  917. {
  918. delete mFieldDictionary;
  919. AssertFatal(nextNameObject == (SimObject*)-1,avar(
  920. "SimObject::~SimObject: Not removed from dictionary: name %s, id %i",
  921. objectName, mId));
  922. AssertFatal(nextManagerNameObject == (SimObject*)-1,avar(
  923. "SimObject::~SimObject: Not removed from manager dictionary: name %s, id %i",
  924. objectName,mId));
  925. AssertFatal(mFlags.test(Added) == 0, "SimObject::object "
  926. "missing call to SimObject::onRemove");
  927. }
  928. //---------------------------------------------------------------------------
  929. bool SimObject::isLocked()
  930. {
  931. if(!mFieldDictionary)
  932. return false;
  933. const char * val = mFieldDictionary->getFieldValue( StringTable->insert( "locked", false ) );
  934. return( val ? dAtob(val) : false );
  935. }
  936. void SimObject::setLocked( bool b = true )
  937. {
  938. setDataField(StringTable->insert("locked", false), NULL, b ? "true" : "false" );
  939. }
  940. bool SimObject::isHidden()
  941. {
  942. if(!mFieldDictionary)
  943. return false;
  944. const char * val = mFieldDictionary->getFieldValue( StringTable->insert( "hidden", false ) );
  945. return( val ? dAtob(val) : false );
  946. }
  947. void SimObject::setHidden(bool b = true)
  948. {
  949. setDataField(StringTable->insert("hidden", false), NULL, b ? "true" : "false" );
  950. }
  951. //---------------------------------------------------------------------------
  952. bool SimObject::onAdd()
  953. {
  954. mFlags.set(Added);
  955. if(getClassRep())
  956. mNameSpace = getClassRep()->getNameSpace();
  957. linkNamespaces();
  958. // onAdd() should return FALSE if there was an error
  959. return true;
  960. }
  961. void SimObject::onRemove()
  962. {
  963. mFlags.clear(Added);
  964. unlinkNamespaces();
  965. }
  966. void SimObject::onGroupAdd()
  967. {
  968. }
  969. void SimObject::onGroupRemove()
  970. {
  971. }
  972. void SimObject::onDeleteNotify(SimObject*)
  973. {
  974. }
  975. void SimObject::onNameChange(const char*)
  976. {
  977. }
  978. void SimObject::onStaticModified(const char* slotName, const char* newValue)
  979. {
  980. }
  981. bool SimObject::processArguments(S32 argc, const char**)
  982. {
  983. return argc == 0;
  984. }
  985. bool SimObject::isChildOfGroup(SimGroup* pGroup)
  986. {
  987. if(!pGroup)
  988. return false;
  989. //if we *are* the group in question,
  990. //return true:
  991. if(pGroup == dynamic_cast<SimGroup*>(this))
  992. return true;
  993. SimGroup* temp = mGroup;
  994. while(temp)
  995. {
  996. if(temp == pGroup)
  997. return true;
  998. temp = temp->mGroup;
  999. }
  1000. return false;
  1001. }
  1002. ConsoleMethod(SimObject, isChildOfGroup, bool, 3,3," returns true, if we are in the specified simgroup - or a subgroup thereof")
  1003. {
  1004. SimGroup* pGroup = dynamic_cast<SimGroup*>(Sim::findObject(dAtoi(argv[2])));
  1005. if(pGroup)
  1006. {
  1007. return object->isChildOfGroup(pGroup);
  1008. }
  1009. return false;
  1010. }
  1011. //---------------------------------------------------------------------------
  1012. static Chunker<SimObject::Notify> notifyChunker(128000);
  1013. SimObject::Notify *SimObject::mNotifyFreeList = NULL;
  1014. SimObject::Notify *SimObject::allocNotify()
  1015. {
  1016. if(mNotifyFreeList)
  1017. {
  1018. SimObject::Notify *ret = mNotifyFreeList;
  1019. mNotifyFreeList = ret->next;
  1020. return ret;
  1021. }
  1022. return notifyChunker.alloc();
  1023. }
  1024. void SimObject::freeNotify(SimObject::Notify* note)
  1025. {
  1026. AssertFatal(note->type != SimObject::Notify::Invalid, "Invalid notify");
  1027. note->type = SimObject::Notify::Invalid;
  1028. note->next = mNotifyFreeList;
  1029. mNotifyFreeList = note;
  1030. }
  1031. //------------------------------------------------------------------------------
  1032. SimObject::Notify* SimObject::removeNotify(void *ptr, SimObject::Notify::Type type)
  1033. {
  1034. Notify **list = &mNotifyList;
  1035. while(*list)
  1036. {
  1037. if((*list)->ptr == ptr && (*list)->type == type)
  1038. {
  1039. SimObject::Notify *ret = *list;
  1040. *list = ret->next;
  1041. return ret;
  1042. }
  1043. list = &((*list)->next);
  1044. }
  1045. return NULL;
  1046. }
  1047. void SimObject::deleteNotify(SimObject* obj)
  1048. {
  1049. AssertFatal(!obj->isDeleted(),
  1050. "SimManager::deleteNotify: Object is being deleted");
  1051. Notify *note = allocNotify();
  1052. note->ptr = (void *) this;
  1053. note->next = obj->mNotifyList;
  1054. note->type = Notify::DeleteNotify;
  1055. obj->mNotifyList = note;
  1056. note = allocNotify();
  1057. note->ptr = (void *) obj;
  1058. note->next = mNotifyList;
  1059. note->type = Notify::ClearNotify;
  1060. mNotifyList = note;
  1061. //obj->deleteNotifyList.pushBack(this);
  1062. //clearNotifyList.pushBack(obj);
  1063. }
  1064. void SimObject::registerReference(SimObject **ptr)
  1065. {
  1066. Notify *note = allocNotify();
  1067. note->ptr = (void *) ptr;
  1068. note->next = mNotifyList;
  1069. note->type = Notify::ObjectRef;
  1070. mNotifyList = note;
  1071. }
  1072. void SimObject::unregisterReference(SimObject **ptr)
  1073. {
  1074. Notify *note = removeNotify((void *) ptr, Notify::ObjectRef);
  1075. if(note)
  1076. freeNotify(note);
  1077. }
  1078. void SimObject::clearNotify(SimObject* obj)
  1079. {
  1080. Notify *note = obj->removeNotify((void *) this, Notify::DeleteNotify);
  1081. if(note)
  1082. freeNotify(note);
  1083. note = removeNotify((void *) obj, Notify::ClearNotify);
  1084. if(note)
  1085. freeNotify(note);
  1086. }
  1087. void SimObject::processDeleteNotifies()
  1088. {
  1089. // clear out any delete notifies and
  1090. // object refs.
  1091. while(mNotifyList)
  1092. {
  1093. Notify *note = mNotifyList;
  1094. mNotifyList = note->next;
  1095. AssertFatal(note->type != Notify::ClearNotify, "Clear notes should be all gone.");
  1096. if(note->type == Notify::DeleteNotify)
  1097. {
  1098. SimObject *obj = (SimObject *) note->ptr;
  1099. Notify *cnote = obj->removeNotify((void *)this, Notify::ClearNotify);
  1100. obj->onDeleteNotify(this);
  1101. freeNotify(cnote);
  1102. }
  1103. else
  1104. {
  1105. // it must be an object ref - a pointer refs this object
  1106. *((SimObject **) note->ptr) = NULL;
  1107. }
  1108. freeNotify(note);
  1109. }
  1110. }
  1111. void SimObject::clearAllNotifications()
  1112. {
  1113. for(Notify **cnote = &mNotifyList; *cnote; )
  1114. {
  1115. Notify *temp = *cnote;
  1116. if(temp->type == Notify::ClearNotify)
  1117. {
  1118. *cnote = temp->next;
  1119. Notify *note = ((SimObject *) temp->ptr)->removeNotify((void *) this, Notify::DeleteNotify);
  1120. freeNotify(temp);
  1121. freeNotify(note);
  1122. }
  1123. else
  1124. cnote = &(temp->next);
  1125. }
  1126. }
  1127. //---------------------------------------------------------------------------
  1128. void SimObject::initPersistFields()
  1129. {
  1130. Parent::initPersistFields();
  1131. addGroup("SimBase");
  1132. addField("canSaveDynamicFields", TypeBool, Offset(mCanSaveFieldDictionary, SimObject), &writeCanSaveDynamicFields, "");
  1133. addField("internalName", TypeString, Offset(mInternalName, SimObject), &writeInternalName, "");
  1134. addProtectedField("parentGroup", TypeSimObjectPtr, Offset(mGroup, SimObject), &setParentGroup, &defaultProtectedGetFn, &writeParentGroup, "Group hierarchy parent of the object." );
  1135. endGroup("SimBase");
  1136. // Namespace Linking.
  1137. addGroup("Namespace Linking");
  1138. addProtectedField("superclass", TypeString, Offset(mSuperClassName, SimObject), &setSuperClass, &defaultProtectedGetFn, &writeSuperclass, "Script Class of object.");
  1139. addProtectedField("class", TypeString, Offset(mClassName, SimObject), &setClass, &defaultProtectedGetFn, &writeClass, "Script SuperClass of object.");
  1140. endGroup("Namespace Linking");
  1141. }
  1142. //-----------------------------------------------------------------------------
  1143. SimObject* SimObject::clone( const bool copyDynamicFields )
  1144. {
  1145. // Craete cloned object.
  1146. SimObject* pCloneObject = dynamic_cast<SimObject*>( ConsoleObject::create(getClassName()) );
  1147. if (!pCloneObject)
  1148. {
  1149. Con::errorf("SimObject::clone() - Unable to create cloned object.");
  1150. return NULL;
  1151. }
  1152. // Register object.
  1153. if ( !pCloneObject->registerObject() )
  1154. {
  1155. Con::warnf("SimObject::clone() - Unable to register cloned object.");
  1156. delete pCloneObject;
  1157. return NULL;
  1158. }
  1159. // Copy object.
  1160. copyTo( pCloneObject );
  1161. // Copy over dynamic fields if requested.
  1162. if ( copyDynamicFields )
  1163. pCloneObject->assignDynamicFieldsFrom( this );
  1164. return pCloneObject;
  1165. }
  1166. //-----------------------------------------------------------------------------
  1167. ConsoleMethod(SimObject, clone, S32, 2, 3, "([bool copyDynamicFields? = false]) - Clones the object.\n"
  1168. "@param copyDynamicFields Whether the dynamic fields should be copied to the cloned object or not. Optional: Defaults to false.\n"
  1169. "@return (newObjectID) The newly cloned object's id if successful, otherwise a 0.")
  1170. {
  1171. // Fetch copy dynamic fields flag.
  1172. const bool copyDynamicFields = ( argc >= 3 ) ? dAtob( argv[2] ) : false;
  1173. // Clone Object.
  1174. SimObject* pClonedObject = object->clone( copyDynamicFields );
  1175. // Finish if object was not cloned.
  1176. if ( pClonedObject == NULL )
  1177. return 0;
  1178. return pClonedObject->getId();
  1179. }
  1180. //-----------------------------------------------------------------------------
  1181. void SimObject::copyTo(SimObject* object)
  1182. {
  1183. object->mClassName = mClassName;
  1184. object->mSuperClassName = mSuperClassName;
  1185. object->linkNamespaces();
  1186. }
  1187. //-----------------------------------------------------------------------------
  1188. bool SimObject::setParentGroup(void* obj, const char* data)
  1189. {
  1190. SimGroup *parent = NULL;
  1191. SimObject *object = static_cast<SimObject*>(obj);
  1192. if(Sim::findObject(data, parent))
  1193. parent->addObject(object);
  1194. // always return false, because we've set mGroup when we called addObject
  1195. return false;
  1196. }
  1197. bool SimObject::addToSet(SimObjectId spid)
  1198. {
  1199. if (mFlags.test(Added) == false)
  1200. return false;
  1201. SimObject* ptr = Sim::findObject(spid);
  1202. if (ptr)
  1203. {
  1204. SimSet* sp = dynamic_cast<SimSet*>(ptr);
  1205. AssertFatal(sp != 0,
  1206. "SimObject::addToSet: "
  1207. "ObjectId does not refer to a set object");
  1208. if (sp)
  1209. {
  1210. sp->addObject(this);
  1211. return true;
  1212. }
  1213. }
  1214. return false;
  1215. }
  1216. bool SimObject::addToSet(const char *ObjectName)
  1217. {
  1218. if (mFlags.test(Added) == false)
  1219. return false;
  1220. SimObject* ptr = Sim::findObject(ObjectName);
  1221. if (ptr)
  1222. {
  1223. SimSet* sp = dynamic_cast<SimSet*>(ptr);
  1224. AssertFatal(sp != 0,
  1225. "SimObject::addToSet: "
  1226. "ObjectName does not refer to a set object");
  1227. if (sp)
  1228. {
  1229. sp->addObject(this);
  1230. return true;
  1231. }
  1232. }
  1233. return false;
  1234. }
  1235. bool SimObject::removeFromSet(SimObjectId sid)
  1236. {
  1237. if (mFlags.test(Added) == false)
  1238. return false;
  1239. SimSet *set;
  1240. if(Sim::findObject(sid, set))
  1241. {
  1242. set->removeObject(this);
  1243. return true;
  1244. }
  1245. return false;
  1246. }
  1247. bool SimObject::removeFromSet(const char *objectName)
  1248. {
  1249. if (mFlags.test(Added) == false)
  1250. return false;
  1251. SimSet *set;
  1252. if(Sim::findObject(objectName, set))
  1253. {
  1254. set->removeObject(this);
  1255. return true;
  1256. }
  1257. return false;
  1258. }
  1259. void SimObject::inspectPreApply()
  1260. {
  1261. }
  1262. void SimObject::inspectPostApply()
  1263. {
  1264. }
  1265. void SimObject::linkNamespaces()
  1266. {
  1267. if( mNameSpace )
  1268. unlinkNamespaces();
  1269. StringTableEntry parent = StringTable->insert( getClassName() );
  1270. if( ( mNSLinkMask & LinkSuperClassName ) && mSuperClassName && mSuperClassName[0] )
  1271. {
  1272. if( Con::linkNamespaces( parent, mSuperClassName ) )
  1273. parent = mSuperClassName;
  1274. else
  1275. mSuperClassName = StringTable->EmptyString; // CodeReview Is this behavior that we want?
  1276. // CodeReview This will result in the mSuperClassName variable getting hosed
  1277. // CodeReview if Con::linkNamespaces returns false. Looking at the code for
  1278. // CodeReview Con::linkNamespaces, and the call it makes to classLinkTo, it seems
  1279. // CodeReview like this would only fail if it had bogus data to begin with, but
  1280. // CodeReview I wanted to note this behavior which occurs in some implementations
  1281. // CodeReview but not all. -patw
  1282. }
  1283. // ClassName -> SuperClassName
  1284. if ( ( mNSLinkMask & LinkClassName ) && mClassName && mClassName[0] )
  1285. {
  1286. if( Con::linkNamespaces( parent, mClassName ) )
  1287. parent = mClassName;
  1288. else
  1289. mClassName = StringTable->EmptyString; // CodeReview (See previous note on this code)
  1290. }
  1291. // ObjectName -> ClassName
  1292. StringTableEntry objectName = getName();
  1293. if( objectName && objectName[0] )
  1294. {
  1295. if( Con::linkNamespaces( parent, objectName ) )
  1296. parent = objectName;
  1297. }
  1298. // Store our namespace.
  1299. mNameSpace = Con::lookupNamespace( parent );
  1300. }
  1301. void SimObject::unlinkNamespaces()
  1302. {
  1303. if (!mNameSpace)
  1304. return;
  1305. // Restore NameSpace's
  1306. StringTableEntry child = getName();
  1307. if( child && child[0] )
  1308. {
  1309. if( ( mNSLinkMask & LinkClassName ) && mClassName && mClassName[0])
  1310. {
  1311. if( Con::unlinkNamespaces( mClassName, child ) )
  1312. child = mClassName;
  1313. }
  1314. if( ( mNSLinkMask & LinkSuperClassName ) && mSuperClassName && mSuperClassName[0] )
  1315. {
  1316. if( Con::unlinkNamespaces( mSuperClassName, child ) )
  1317. child = mSuperClassName;
  1318. }
  1319. Con::unlinkNamespaces( getClassName(), child );
  1320. }
  1321. else
  1322. {
  1323. child = mClassName;
  1324. if( child && child[0] )
  1325. {
  1326. if( ( mNSLinkMask & LinkSuperClassName ) && mSuperClassName && mSuperClassName[0] )
  1327. {
  1328. if( Con::unlinkNamespaces( mSuperClassName, child ) )
  1329. child = mSuperClassName;
  1330. }
  1331. Con::unlinkNamespaces( getClassName(), child );
  1332. }
  1333. else
  1334. {
  1335. if( ( mNSLinkMask & LinkSuperClassName ) && mSuperClassName && mSuperClassName[0] )
  1336. Con::unlinkNamespaces( getClassName(), mSuperClassName );
  1337. }
  1338. }
  1339. mNameSpace = NULL;
  1340. }
  1341. void SimObject::setClassNamespace( const char *classNamespace )
  1342. {
  1343. mClassName = StringTable->insert( classNamespace );
  1344. linkNamespaces();
  1345. }
  1346. void SimObject::setSuperClassNamespace( const char *superClassNamespace )
  1347. {
  1348. mSuperClassName = StringTable->insert( superClassNamespace );
  1349. linkNamespaces();
  1350. }
  1351. ConsoleMethod(SimObject, getClassNamespace, const char*, 2, 2, "")
  1352. {
  1353. return object->getClassNamespace();
  1354. }
  1355. ConsoleMethod(SimObject, getSuperClassNamespace, const char*, 2, 2, "")
  1356. {
  1357. return object->getSuperClassNamespace();
  1358. }
  1359. ConsoleMethod(SimObject, setClassNamespace, void, 2, 3, "")
  1360. {
  1361. object->setClassNamespace(argv[2]);
  1362. }
  1363. ConsoleMethod(SimObject, setSuperClassNamespace, void, 2, 3, "")
  1364. {
  1365. object->setSuperClassNamespace(argv[2]);
  1366. }
  1367. //-----------------------------------------------------------------------------
  1368. ConsoleMethod(SimObject, setProgenitorFile, void, 3, 3, "(file) Sets the progenitor file responsible for this instances creation.\n"
  1369. "@param file The progenitor file responsible for this instances creation.\n"
  1370. "@return No return value." )
  1371. {
  1372. object->setProgenitorFile( argv[2] );
  1373. }
  1374. //-----------------------------------------------------------------------------
  1375. ConsoleMethod(SimObject, getProgenitorFile, const char*, 2, 2, "() Gets the progenitor file responsible for this instances creation.\n"
  1376. "@return The progenitor file responsible for this instances creation." )
  1377. {
  1378. return object->getProgenitorFile();
  1379. }
  1380. //-----------------------------------------------------------------------------
  1381. ConsoleMethod(SimObject, startTimer, bool, 4, 5, "(callbackFunction, float timePeriod, [repeat]) - Starts a periodic timer for this object.\n"
  1382. "Sets a timer on the object that, when it expires, will cause the object to execute the onTimer() callback.\n"
  1383. "The timer event will continue to occur at regular intervals until setTimerOff() is called.\n"
  1384. "@param callbackFunction The name of the callback function to call for each timer repetition.\n"
  1385. "@param timePeriod The period of time (in milliseconds) between each callback.\n"
  1386. "@param repeat The number of times the timer should repeat. If not specified or zero then it will run infinitely\n"
  1387. "@return No return Value.")
  1388. {
  1389. // Is the periodic timer running?
  1390. if ( object->getPeriodicTimerID() != 0 )
  1391. {
  1392. // Yes, so cancel it.
  1393. Sim::cancelEvent( object->getPeriodicTimerID() );
  1394. // Reset Timer ID.
  1395. object->setPeriodicTimerID( 0 );
  1396. }
  1397. // Fetch the callback function.
  1398. StringTableEntry callbackFunction = StringTable->insert( argv[2] );
  1399. // Does the function exist?
  1400. if ( !object->isMethod( callbackFunction ) )
  1401. {
  1402. // No, so warn.
  1403. Con::warnf("SimObject::startTimer() - The callback function of '%s' does not exist.", callbackFunction );
  1404. return false;
  1405. }
  1406. // Fetch the time period.
  1407. const S32 timePeriod = dAtoi(argv[3]);
  1408. // Is the time period valid?
  1409. if ( timePeriod < 1 )
  1410. {
  1411. // No, so warn.
  1412. Con::warnf("SimObject::startTimer() - The time period of '%d' is invalid.", timePeriod );
  1413. return false;
  1414. }
  1415. // Fetch the repeat count.
  1416. const S32 repeat = argc >= 5 ? dAtoi(argv[4]) : 0;
  1417. // Create Timer Event.
  1418. SimObjectTimerEvent* pEvent = new SimObjectTimerEvent( callbackFunction, (U32)timePeriod, (U32)repeat );
  1419. // Post Event.
  1420. object->setPeriodicTimerID( Sim::postEvent( object, pEvent, Sim::getCurrentTime() + timePeriod ) );
  1421. return true;
  1422. }
  1423. //-----------------------------------------------------------------------------
  1424. ConsoleMethod(SimObject, stopTimer, void, 2, 2, "() - Stops the periodic timer for this object.\n"
  1425. "@return No return Value.")
  1426. {
  1427. // Finish if the periodic timer isn't running.
  1428. if ( object->getPeriodicTimerID() == 0 )
  1429. return;
  1430. // Cancel It.
  1431. Sim::cancelEvent( object->getPeriodicTimerID() );
  1432. // Reset Timer ID.
  1433. object->setPeriodicTimerID( 0 );
  1434. }
  1435. //-----------------------------------------------------------------------------
  1436. ConsoleMethod(SimObject, isTimerActive, bool, 2, 2, "() - Checks whether the periodic timer is active for this object or not.\n"
  1437. "@return Whether the periodic timer is active for this object or not.")
  1438. {
  1439. return object->isPeriodicTimerActive();
  1440. }