metaScripting_ScriptBinding.cc 29 KB

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