metaScripting_ScriptBinding.cc 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  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 "platform/platform.h"
  23. #include "console/console.h"
  24. #include "console/consoleInternal.h"
  25. #include "console/ast.h"
  26. #include "io/resource/resourceManager.h"
  27. #include "io/fileStream.h"
  28. #include "console/compiler.h"
  29. // Buffer for expanding script filenames.
  30. static char pathBuffer[1024];
  31. static U32 execDepth = 0;
  32. extern S32 QSORT_CALLBACK ACRCompare(const void *aptr, const void *bptr);
  33. ConsoleFunctionGroupBegin(MetaScripting, "Functions that let you manipulate the scripting engine programmatically.");
  34. /*! @defgroup MetaScriptingFunctions Meta-Scripting
  35. @ingroup TorqueScriptFunctions
  36. @{
  37. */
  38. /*! Use the call function to dynamically build and call a function.
  39. @param funcName A string containing the unadorned name of a function to be executed.
  40. @param args .. - Any arguments that should be passed to the function.
  41. @return Returns a string containing the results from the function that is built and called.
  42. @sa eval
  43. */
  44. ConsoleFunctionWithDocs(call, ConsoleString, 2, 0, ( funcName, [args ... ]?))
  45. {
  46. return Con::execute(argc - 1, argv + 1);
  47. }
  48. static StringTableEntry getDSOPath(const char *scriptPath)
  49. {
  50. const char *slash = dStrrchr(scriptPath, '/');
  51. return StringTable->insertn(scriptPath, slash - scriptPath, true);
  52. }
  53. /*! Returns the DSO path of the given filename
  54. @param scriptFileName A string contains the file to search for
  55. @return Returns either the requested pathname as a string, or an empty string if not found.
  56. */
  57. ConsoleFunctionWithDocs(getDSOPath, ConsoleString, 2, 2, (scriptFileName))
  58. {
  59. Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]);
  60. const char *filename = getDSOPath(pathBuffer);
  61. if(filename == NULL || *filename == 0)
  62. return "";
  63. return filename;
  64. }
  65. /*! Use the compile function to pre-compile a script file without executing the contents.
  66. @param fileName A path to the script to compile.
  67. @return Returns 1 if the script compiled without errors and 0 if the file did not compile correctly or if the path is wrong. Also, ff the path is invalid, an error will print to the console.
  68. @sa exec
  69. */
  70. ConsoleFunctionWithDocs(compile, ConsoleBool, 2, 2, ( fileName ))
  71. {
  72. TORQUE_UNUSED( argc );
  73. char nameBuffer[512];
  74. char* script = NULL;
  75. U32 scriptSize = 0;
  76. FileTime comModifyTime, scrModifyTime;
  77. Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]);
  78. // Figure out where to put DSOs
  79. StringTableEntry dsoPath = getDSOPath(pathBuffer);
  80. // If the script file extention is '.ed.cs' then compile it to a different compiled extention
  81. bool isEditorScript = false;
  82. const char *ext = dStrrchr( pathBuffer, '.' );
  83. if( ext && ( dStricmp( ext, ".cs" ) == 0 ) )
  84. {
  85. const char* ext2 = ext - 3;
  86. if( dStricmp( ext2, ".ed.cs" ) == 0 )
  87. isEditorScript = true;
  88. }
  89. else if( ext && ( dStricmp( ext, ".gui" ) == 0 ) )
  90. {
  91. const char* ext2 = ext - 3;
  92. if( dStricmp( ext2, ".ed.gui" ) == 0 )
  93. isEditorScript = true;
  94. }
  95. const char *filenameOnly = dStrrchr(pathBuffer, '/');
  96. if(filenameOnly)
  97. ++filenameOnly;
  98. else
  99. filenameOnly = pathBuffer;
  100. if( isEditorScript )
  101. dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".edso", NULL);
  102. else
  103. dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".dso", NULL);
  104. ResourceObject *rScr = ResourceManager->find(pathBuffer);
  105. ResourceObject *rCom = ResourceManager->find(nameBuffer);
  106. if(rCom)
  107. rCom->getFileTimes(NULL, &comModifyTime);
  108. if(rScr)
  109. rScr->getFileTimes(NULL, &scrModifyTime);
  110. Stream *s = ResourceManager->openStream(pathBuffer);
  111. if(s)
  112. {
  113. scriptSize = ResourceManager->getSize(pathBuffer);
  114. script = new char [scriptSize+1];
  115. s->read(scriptSize, script);
  116. ResourceManager->closeStream(s);
  117. script[scriptSize] = 0;
  118. }
  119. if (!scriptSize || !script)
  120. {
  121. delete [] script;
  122. Con::errorf(ConsoleLogEntry::Script, "compile: invalid script file %s.", pathBuffer);
  123. return false;
  124. }
  125. // compile this baddie.
  126. // -Mat reducing console noise
  127. #if defined(TORQUE_DEBUG)
  128. Con::printf("Compiling %s...", pathBuffer);
  129. #endif
  130. CodeBlock *code = new CodeBlock();
  131. code->compile(nameBuffer, pathBuffer, script);
  132. delete code;
  133. code = NULL;
  134. delete[] script;
  135. return true;
  136. }
  137. /*!
  138. */
  139. ConsoleFunctionWithDocs(compilePath, ConsoleString, 2, 2, ( path ))
  140. {
  141. if ( !Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]) )
  142. return "-1 0";
  143. const char *compileArgs[2] = { "compile", NULL };
  144. S32 failedScripts = 0;
  145. S32 totalScripts = 0;
  146. ResourceObject *match = NULL;
  147. while ( (match = ResourceManager->findMatch( pathBuffer, &compileArgs[1], match )) )
  148. {
  149. if ( !ccompile( NULL, 1, compileArgs ) )
  150. failedScripts++;
  151. totalScripts++;
  152. }
  153. char* result = Con::getReturnBuffer(32);
  154. dSprintf( result, 32, "%d %d", failedScripts, totalScripts );
  155. return result;
  156. }
  157. static bool scriptExecutionEcho = false;
  158. /*! Whether to echo script file execution or not.
  159. */
  160. ConsoleFunctionWithDocs(setScriptExecEcho, ConsoleVoid, 2, 2, (echo?))
  161. {
  162. scriptExecutionEcho = dAtob(argv[1]);
  163. }
  164. /*! Use the exec function to compile and execute a normal script, or a special journal script.
  165. If $Pref::ignoreDSOs is set to true, the system will use .cs before a .dso file if both are found.
  166. @param fileName A string containing a path to the script to be compiled and executed.
  167. @param nocalls A boolean value. If this value is set to true, then all function calls encountered while executing the script file will be skipped and not called. This allows us to re-define function definitions found in a script file, without re-executing other worker scripts in the same file.
  168. @param journalScript A boolean value. If this value is set tot true, and if a journal is being played, the engine will attempt to read this script from the journal stream. If no journal is playing, this field is ignored.
  169. @return Returns true if the file compiled and executed w/o errors, false otherwise.
  170. @sa compile
  171. */
  172. ConsoleFunctionWithDocs(exec, ConsoleBool, 2, 4, ( fileName, [nocalls]?, [journalScript ]?))
  173. {
  174. execDepth++;
  175. #ifdef TORQUE_ALLOW_JOURNALING
  176. bool journal = false;
  177. if(journalDepth >= execDepth)
  178. journalDepth = execDepth + 1;
  179. else
  180. journal = true;
  181. #endif //TORQUE_ALLOW_JOURNALING
  182. bool noCalls = false;
  183. bool ret = false;
  184. if(argc >= 3 && dAtoi(argv[2]))
  185. noCalls = true;
  186. #ifdef TORQUE_ALLOW_JOURNALING
  187. if(argc >= 4 && dAtoi(argv[3]) && !journal)
  188. {
  189. journal = true;
  190. journalDepth = execDepth;
  191. }
  192. #endif //TORQUE_ALLOW_JOURNALING
  193. // Determine the filename we actually want...
  194. Con::expandPath(pathBuffer, sizeof(pathBuffer), argv[1]);
  195. // Figure out where to put DSOs
  196. StringTableEntry dsoPath = getDSOPath(pathBuffer);
  197. const char *ext = dStrrchr(pathBuffer, '.');
  198. if(!ext)
  199. {
  200. // We need an extension!
  201. Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file name %s.", pathBuffer);
  202. execDepth--;
  203. return false;
  204. }
  205. // Check Editor Extensions
  206. bool isEditorScript = false;
  207. #ifdef TORQUE_ALLOW_DSO_GENERATION
  208. // If the script file extension is '.ed.cs' then compile it to a different compiled extension
  209. if( dStricmp( ext, ".cs" ) == 0 )
  210. {
  211. const char* ext2 = ext - 3;
  212. if( dStricmp( ext2, ".ed.cs" ) == 0 )
  213. isEditorScript = true;
  214. }
  215. else if( dStricmp( ext, ".gui" ) == 0 )
  216. {
  217. const char* ext2 = ext - 3;
  218. if( dStricmp( ext2, ".ed.gui" ) == 0 )
  219. isEditorScript = true;
  220. }
  221. #endif //TORQUE_ALLOW_DSO_GENERATION
  222. // rdbhack: if we can't find the script file in the game directory, look for it
  223. // in the Application Data directory. This makes it possible to keep the user
  224. // ignorant of where the files are actually saving to, thus eliminating the need
  225. // for the script functions: execPrefs, getUserDataDirectory, etc.
  226. //
  227. // This works because we know that script files located in the prefs path will
  228. // not have compiled versions (it checks for this further down). Otherwise this
  229. // would be a big problem!
  230. StringTableEntry scriptFileName = StringTable->EmptyString;
  231. //Luma : This is redundant, we wont be building dso's on the device -
  232. //plus saving dso to the user directory when attempting build for the
  233. //release tests on iPhone is irrelevant.
  234. #ifdef TORQUE_ALLOW_DSO_GENERATION
  235. if(!ResourceManager->find(pathBuffer))
  236. {
  237. // NOTE: this code is pretty much a duplication of code much further down in this
  238. // function...
  239. // our work just got a little harder.. if we couldn't find the .cs, then we need to
  240. // also look for the .dso BEFORE we can try the prefs path.. UGH
  241. const char *filenameOnly = dStrrchr(pathBuffer, '/');
  242. if(filenameOnly)
  243. ++filenameOnly;
  244. else
  245. filenameOnly = pathBuffer;
  246. // we could skip this step and rid ourselves of a bunch of nonsense but we can't be
  247. // certain the dso path is the same as the path given to use in scriptFileNameBuffer
  248. char pathAndFilename[1024];
  249. Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath);
  250. char nameBuffer[1024];
  251. if( isEditorScript ) // this should never be the case since we are a PLAYER not a TOOL, but you never know
  252. dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL);
  253. else
  254. dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL);
  255. if(!ResourceManager->find(nameBuffer))
  256. scriptFileName = Platform::getPrefsPath(Platform::stripBasePath(pathBuffer));
  257. else
  258. scriptFileName = StringTable->insert(pathBuffer);
  259. }
  260. else
  261. scriptFileName = StringTable->insert(pathBuffer);
  262. #else //TORQUE_ALLOW_DSO_GENERATION
  263. //Luma : Just insert the file name.
  264. scriptFileName = StringTable->insert(pathBuffer);
  265. #endif //TORQUE_ALLOW_DSO_GENERATION
  266. //Luma : Something screwed up so get out early
  267. if(scriptFileName == NULL || *scriptFileName == 0)
  268. {
  269. execDepth--;
  270. return false;
  271. }
  272. #ifdef TORQUE_ALLOW_JOURNALING
  273. bool compiled = dStricmp(ext, ".mis") && !journal && !Con::getBoolVariable("Scripts::ignoreDSOs");
  274. #else
  275. bool compiled = dStricmp(ext, ".mis") && !Con::getBoolVariable("Scripts::ignoreDSOs");
  276. #endif //TORQUE_ALLOW_JOURNALING
  277. // [tom, 12/5/2006] stripBasePath() messes up if the filename is not in the exe
  278. // path, current directory or prefs path. Thus, getDSOFilename() will also mess
  279. // up and so this allows the scripts to still load but without a DSO.
  280. if(Platform::isFullPath(Platform::stripBasePath(pathBuffer)))
  281. compiled = false;
  282. // [tom, 11/17/2006] It seems to make sense to not compile scripts that are in the
  283. // prefs directory. However, getDSOPath() can handle this situation and will put
  284. // the dso along with the script to avoid name clashes with tools/game dsos.
  285. #ifdef TORQUE_ALLOW_DSO_GENERATION
  286. // Is this a file we should compile? (anything in the prefs path should not be compiled)
  287. StringTableEntry prefsPath = Platform::getPrefsPath();
  288. if( dStrlen(prefsPath) > 0 && dStrnicmp(scriptFileName, prefsPath, dStrlen(prefsPath)) == 0)
  289. compiled = false;
  290. #endif //TORQUE_ALLOW_DSO_GENERATION
  291. // If we're in a journaling mode, then we will read the script
  292. // from the journal file.
  293. #ifdef TORQUE_ALLOW_JOURNALING
  294. if(journal && Game->isJournalReading())
  295. {
  296. char fileNameBuf[256];
  297. bool fileRead;
  298. U32 fileSize;
  299. Game->getJournalStream()->readString(fileNameBuf);
  300. Game->getJournalStream()->read(&fileRead);
  301. if(!fileRead)
  302. {
  303. Con::errorf(ConsoleLogEntry::Script, "Journal script read (failed) for %s", fileNameBuf);
  304. execDepth--;
  305. return false;
  306. }
  307. Game->journalRead(&fileSize);
  308. char *script = new char[fileSize + 1];
  309. Game->journalRead(fileSize, script);
  310. script[fileSize] = 0;
  311. Con::printf("Executing (journal-read) %s.", scriptFileName);
  312. CodeBlock *newCodeBlock = new CodeBlock();
  313. newCodeBlock->compileExec(scriptFileName, script, noCalls, 0);
  314. delete [] script;
  315. execDepth--;
  316. return true;
  317. }
  318. #endif //TORQUE_ALLOW_JOURNALING
  319. // Ok, we let's try to load and compile the script.
  320. ResourceObject *rScr = ResourceManager->find(scriptFileName);
  321. ResourceObject *rCom = NULL;
  322. char nameBuffer[512];
  323. char* script = NULL;
  324. U32 scriptSize = 0;
  325. U32 version;
  326. Stream *compiledStream = NULL;
  327. FileTime comModifyTime, scrModifyTime;
  328. // Check here for .edso
  329. //bool edso = false;
  330. //if( dStricmp( ext, ".edso" ) == 0 && rScr )
  331. //{
  332. // edso = true;
  333. // rCom = rScr;
  334. // rScr = NULL;
  335. // rCom->getFileTimes( NULL, &comModifyTime );
  336. // dStrcpy( nameBuffer, scriptFileName );
  337. //}
  338. // If we're supposed to be compiling this file, check to see if there's a DSO
  339. if(compiled /*&& !edso*/)
  340. {
  341. const char *filenameOnly = dStrrchr(scriptFileName, '/');
  342. if(filenameOnly)
  343. ++filenameOnly; //remove the / at the front
  344. else
  345. filenameOnly = scriptFileName;
  346. char pathAndFilename[1024];
  347. Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath);
  348. if( isEditorScript )
  349. dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL);
  350. else
  351. dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL);
  352. rCom = ResourceManager->find(nameBuffer);
  353. if(rCom)
  354. rCom->getFileTimes(NULL, &comModifyTime);
  355. if(rScr)
  356. rScr->getFileTimes(NULL, &scrModifyTime);
  357. }
  358. // Let's do a sanity check to complain about DSOs in the future.
  359. //
  360. // MM: This doesn't seem to be working correctly for now so let's just not issue
  361. // the warning until someone knows how to resolve it.
  362. //
  363. //if(compiled && rCom && rScr && Platform::compareFileTimes(comModifyTime, scrModifyTime) < 0)
  364. //{
  365. //Con::warnf("exec: Warning! Found a DSO from the future! (%s)", nameBuffer);
  366. //}
  367. // If we had a DSO, let's check to see if we should be reading from it.
  368. if((compiled && rCom) && (!rScr || Platform::compareFileTimes(comModifyTime, scrModifyTime) >= 0))
  369. {
  370. compiledStream = ResourceManager->openStream(nameBuffer);
  371. if (compiledStream)
  372. {
  373. // Check the version!
  374. compiledStream->read(&version);
  375. if(version != DSO_VERSION)
  376. {
  377. Con::warnf("exec: Found an old DSO (%s, ver %d < %d), ignoring.", nameBuffer, version, DSO_VERSION);
  378. ResourceManager->closeStream(compiledStream);
  379. compiledStream = NULL;
  380. }
  381. }
  382. }
  383. #ifdef TORQUE_ALLOW_JOURNALING
  384. // If we're journalling, let's write some info out.
  385. if(journal && Game->isJournalWriting())
  386. Game->getJournalStream()->writeString(scriptFileName);
  387. #endif //TORQUE_ALLOW_JOURNALING
  388. if(rScr && !compiledStream)
  389. {
  390. // If we have source but no compiled version, then we need to compile
  391. // (and journal as we do so, if that's required).
  392. //Con::errorf( "No DSO found! : %s", scriptFileName );
  393. Stream *s = ResourceManager->openStream(scriptFileName);
  394. #ifdef TORQUE_ALLOW_JOURNALING
  395. if(journal && Game->isJournalWriting())
  396. Game->getJournalStream()->write(bool(s != NULL));
  397. #endif //TORQUE_ALLOW_JOURNALING
  398. if(s)
  399. {
  400. scriptSize = ResourceManager->getSize(scriptFileName);
  401. script = new char [scriptSize+1];
  402. s->read(scriptSize, script);
  403. #ifdef TORQUE_ALLOW_JOURNALING
  404. if(journal && Game->isJournalWriting())
  405. {
  406. Game->journalWrite(scriptSize);
  407. Game->journalWrite(scriptSize, script);
  408. }
  409. #endif //TORQUE_ALLOW_JOURNALING
  410. ResourceManager->closeStream(s);
  411. script[scriptSize] = 0;
  412. }
  413. if (!scriptSize || !script)
  414. {
  415. delete [] script;
  416. Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file %s.", scriptFileName);
  417. execDepth--;
  418. return false;
  419. }
  420. //Luma: Sven -
  421. // no dsos in the editor, seems to fail with so many console changes and version crap. Leaving it to
  422. // work with cs files as is, as they are included with the source either way.
  423. //Also, no DSO generation on iPhone
  424. #ifdef TORQUE_OS_IOS
  425. if(false)
  426. #else
  427. if(compiled)
  428. #endif
  429. {
  430. // compile this baddie.
  431. #if defined(TORQUE_DEBUG)
  432. Con::printf("Compiling %s...", scriptFileName);
  433. #endif
  434. CodeBlock *code = new CodeBlock();
  435. code->compile(nameBuffer, scriptFileName, script);
  436. delete code;
  437. code = NULL;
  438. compiledStream = ResourceManager->openStream(nameBuffer);
  439. if(compiledStream)
  440. {
  441. compiledStream->read(&version);
  442. }
  443. else
  444. {
  445. // We have to exit out here, as otherwise we get double error reports.
  446. delete [] script;
  447. execDepth--;
  448. return false;
  449. }
  450. }
  451. }
  452. else
  453. {
  454. #ifdef TORQUE_ALLOW_JOURNALING
  455. if(journal && Game->isJournalWriting())
  456. Game->getJournalStream()->write(bool(false));
  457. #endif //TORQUE_ALLOW_JOURNALING
  458. }
  459. //Luma : Load compiled script here
  460. if(compiledStream)
  461. {
  462. // Delete the script object first to limit memory used
  463. // during recursive execs.
  464. delete [] script;
  465. script = 0;
  466. // We're all compiled, so let's run it.
  467. //Luma: Profile script executions
  468. F32 st1 = (F32)Platform::getRealMilliseconds();
  469. CodeBlock *code = new CodeBlock;
  470. code->read(scriptFileName, *compiledStream);
  471. ResourceManager->closeStream(compiledStream);
  472. code->exec(0, scriptFileName, NULL, 0, NULL, noCalls, NULL, 0);
  473. F32 et1 = (F32)Platform::getRealMilliseconds();
  474. F32 etf = et1 - st1;
  475. if ( scriptExecutionEcho )
  476. Con::printf("Loaded compiled script %s. Took %.0f ms", scriptFileName, etf);
  477. ret = true;
  478. }
  479. else if(rScr) //Luma : Load normal cs file here.
  480. {
  481. // No compiled script, let's just try executing it
  482. // directly... this is either a mission file, or maybe
  483. // we're on a readonly volume.
  484. CodeBlock *newCodeBlock = new CodeBlock();
  485. StringTableEntry name = StringTable->insert(scriptFileName);
  486. //Luma: Profile script executions
  487. F32 st1 = (F32)Platform::getRealMilliseconds();
  488. newCodeBlock->compileExec(name, script, noCalls, 0);
  489. F32 et1 = (F32)Platform::getRealMilliseconds();
  490. F32 etf = et1 - st1;
  491. if ( scriptExecutionEcho )
  492. Con::printf("Executed %s. Took %.0f ms", scriptFileName, etf);
  493. ret = true;
  494. }
  495. else
  496. {
  497. // Don't have anything.
  498. Con::warnf(ConsoleLogEntry::Script, "Missing file: %s!", pathBuffer);
  499. ret = false;
  500. }
  501. delete [] script;
  502. execDepth--;
  503. return ret;
  504. }
  505. /*! Use the eval function to execute any valid script statement.
  506. If you choose to eval a multi-line statement, be sure that there are no comments or comment blocks embedded in the script string.
  507. @param script A string containing a valid script statement. This may be a single line statement or multiple lines concatenated together with new-line characters.
  508. @return Returns the result of executing the script statement.
  509. @sa call
  510. */
  511. ConsoleFunctionWithDocs(eval, ConsoleString, 2, 2, ( script ))
  512. {
  513. TORQUE_UNUSED( argc );
  514. return Con::evaluate(argv[1], false, NULL);
  515. }
  516. /*! Grabs the relevant data for the variable represented by the given string
  517. @param varName A String representing the variable to check
  518. @return Returns a string containing component data for the requested variable or an empty string if not found.
  519. */
  520. ConsoleFunctionWithDocs(getVariable, ConsoleString, 2, 2, (string varName))
  521. {
  522. return Con::getVariable(argv[1]);
  523. }
  524. /*! Checks whether given name represents a current valid function.
  525. @param funcName The name of the function to check.
  526. @return Returns either true if the string represents a valid function or false if not.
  527. */
  528. ConsoleFunctionWithDocs(isFunction, ConsoleBool, 2, 2, (string funcName))
  529. {
  530. return Con::isFunction(argv[1]);
  531. }
  532. /*! Checks whether the given method name represents a valid method within the given namespace.
  533. @param namespace A string representing the namespace in which the method should reside.
  534. @param method The name of the method in question.
  535. @return Returns a boolean value which is true if the given mathod name represents a valid method in the namespace and false otherwise.
  536. */
  537. ConsoleFunctionWithDocs(isMethod, ConsoleBool, 3, 3, (string namespace, string method))
  538. {
  539. Namespace* ns = Namespace::find( StringTable->insert( argv[1] ) );
  540. Namespace::Entry* nse = ns->lookup( StringTable->insert( argv[2] ) );
  541. if( !nse )
  542. return false;
  543. return true;
  544. }
  545. /*! Attempts to extract a mod directory from path. Returns empty string on failure.
  546. */
  547. ConsoleFunctionWithDocs(getModNameFromPath, ConsoleString, 2, 2, (string path))
  548. {
  549. StringTableEntry modPath = Con::getModNameFromPath(argv[1]);
  550. return modPath ? modPath : "";
  551. }
  552. //----------------------------------------------------------------
  553. /*!
  554. */
  555. ConsoleFunctionWithDocs(getPrefsPath, ConsoleString, 1, 2, ([fileName]?))
  556. {
  557. const char *filename = Platform::getPrefsPath(argc > 1 ? argv[1] : NULL);
  558. if(filename == NULL || *filename == 0)
  559. return "";
  560. return filename;
  561. }
  562. /*!
  563. */
  564. ConsoleFunctionWithDocs(execPrefs, ConsoleBool, 2, 4, (fileName, [nocalls]?, [journalScript]?))
  565. {
  566. const char *filename = Platform::getPrefsPath(argv[1]);
  567. if (filename == NULL || *filename == 0)
  568. return false;
  569. if ( !Platform::isFile(filename) )
  570. return false;
  571. argv[0] = "exec";
  572. argv[1] = filename;
  573. return dAtob(Con::execute(argc, argv));
  574. }
  575. /*! Use the export function to save all global variables matching the specified name pattern in wildCard to a file, either appending to that file or over-writing it.
  576. @param wildCard A string identifying what variable(s) to export. All characters used to create a global are allowed and the special symbol \*\, meaning 0 or more instances of any character.
  577. @param fileName A string containing a path to a file in which to save the globals and their definitions.
  578. @param append A boolean value. If this value is true, the file will be appended to if it exists, otherwise it will be created/over-written.
  579. @return No return value
  580. */
  581. ConsoleFunctionWithDocs(export, ConsoleVoid, 2, 4, ( wildCard, [fileName]?, [append]?))
  582. {
  583. // Fetch the wildcard.
  584. const char* pWildcard = argv[1];
  585. // Fetch the filename.
  586. const char* pFilename = NULL;
  587. if ( argc >= 3 )
  588. {
  589. Con::expandPath( pathBuffer, sizeof(pathBuffer), argv[2] );
  590. pFilename = pathBuffer;
  591. }
  592. // Fetch append flag.
  593. const bool append = argc >= 4 ? dAtob(argv[3] ) : false;
  594. // Export the variables.
  595. gEvalState.globalVars.exportVariables( pWildcard, pFilename, append );
  596. }
  597. /*! Use the deleteVariables function to delete any global variable matching the wildCard statement.
  598. @param wildCard A string identifying what variable(s) to delete. All characters used to create a global are allowed and the special symbol \*\, meaning 0 or more instances of any character.
  599. @return No return value
  600. */
  601. ConsoleFunctionWithDocs(deleteVariables, ConsoleVoid, 2, 2, ( wildCard ))
  602. {
  603. TORQUE_UNUSED( argc );
  604. gEvalState.globalVars.deleteVariables(argv[1]);
  605. }
  606. //----------------------------------------------------------------
  607. /*! Use the trace function to enable (or disable) function call tracing. If enabled, tracing will print a message every time a function is entered, showing what arguments it received, and it will print a message every time a function is exited, showing the return value (or last value of last statement) for that function.
  608. @param enable A boolean value. If set to true, tracing is enabled, otherwise it is disabled.
  609. @return No return value
  610. */
  611. ConsoleFunctionWithDocs(trace, ConsoleVoid, 2, 2, ( enable ))
  612. {
  613. TORQUE_UNUSED( argc );
  614. gEvalState.traceOn = dAtob(argv[1]);
  615. Con::printf("Console trace is %s", gEvalState.traceOn ? "on." : "off.");
  616. }
  617. //----------------------------------------------------------------
  618. #if defined(TORQUE_DEBUG) || defined(INTERNAL_RELEASE)
  619. /*! Use the debug function to cause the engine to issue a debug break and to break into an active debugger.
  620. For this to work, the engine must have been compiled with either TORQUE_DEBUG, or INTERNAL_RELEASE defined
  621. @return No return value.
  622. */
  623. ConsoleFunctionWithDocs(debug, ConsoleVoid, 1, 1, ())
  624. {
  625. TORQUE_UNUSED( argc );
  626. TORQUE_UNUSED( argv );
  627. Platform::debugBreak();
  628. }
  629. #endif
  630. #if defined(TORQUE_OS_IOS) || defined(TORQUE_OS_OSX)
  631. //Some code for allowing torsion to connect, this code returns the ipads local ip address
  632. //code was obtained from http://blog.zachwaugh.com/post/309927273/programmatically-retrieving-ip-address-of-iphone
  633. //adapted to iT2D by me
  634. /*! Gets the Apple hardware local IP on wifi. Should work on OS X and iOS
  635. */
  636. ConsoleFunctionWithDocs(getAppleDeviceIPAddress, ConsoleString, 1, 1, ())
  637. {
  638. char *address = Con::getReturnBuffer(32);
  639. dStrcpy(address, "error");
  640. struct ifaddrs *interfaces = NULL;
  641. struct ifaddrs *temp_addr = NULL;
  642. int success = 0;
  643. // retrieve the current interfaces - returns 0 on success
  644. success = getifaddrs(&interfaces);
  645. if (success == 0)
  646. {
  647. // Loop through linked list of interfaces
  648. temp_addr = interfaces;
  649. while(temp_addr != NULL)
  650. {
  651. if(temp_addr->ifa_addr->sa_family == AF_INET)
  652. {
  653. // Check if interface is en0 which is the wifi connection on the iPhone
  654. // Note: Could be different on MacOSX and simulator and may need modifying
  655. if(dStrcmp(temp_addr->ifa_name, "en0") == 0)
  656. {
  657. dStrcpy(address, inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr));
  658. }
  659. }
  660. temp_addr = temp_addr->ifa_next;
  661. }
  662. }
  663. // Free memory
  664. freeifaddrs(interfaces);
  665. return address;
  666. }
  667. #endif
  668. //----------------------------------------------------------------
  669. /*!
  670. */
  671. ConsoleFunctionWithDocs( enumerateConsoleClasses, ConsoleString, 1, 2, ([baseClass]?))
  672. {
  673. AbstractClassRep *base = NULL;
  674. if(argc > 1)
  675. {
  676. base = AbstractClassRep::findClassRep(argv[1]);
  677. if(!base)
  678. return "";
  679. }
  680. Vector<AbstractClassRep*> classes;
  681. U32 bufSize = 0;
  682. for(AbstractClassRep *rep = AbstractClassRep::getClassList(); rep; rep = rep->getNextClass())
  683. {
  684. if( !base || rep->isClass(base))
  685. {
  686. classes.push_back(rep);
  687. bufSize += dStrlen(rep->getClassName()) + 1;
  688. }
  689. }
  690. if(!classes.size())
  691. return "";
  692. dQsort(classes.address(), classes.size(), sizeof(AbstractClassRep*), ACRCompare);
  693. char* ret = Con::getReturnBuffer(bufSize);
  694. dStrcpy( ret, classes[0]->getClassName());
  695. for( U32 i=0; i< (U32)classes.size(); i++)
  696. {
  697. dStrcat( ret, "\t" );
  698. dStrcat( ret, classes[i]->getClassName() );
  699. }
  700. return ret;
  701. }
  702. /*! @} */ // group MetaScriptFunctions