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