RigsofRods
Soft-body Physics Simulation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ScriptEngine.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2005-2012 Pierre-Michel Ricordel
4  Copyright 2007-2012 Thomas Fischer
5  Copyright 2013-2020 Petr Ohlidal
6 
7  For more information, see http://www.rigsofrods.org/
8 
9  Rigs of Rods is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License version 3, as
11  published by the Free Software Foundation.
12 
13  Rigs of Rods is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
25 
26 #include "ScriptEngine.h"
27 
28 // AS addons start
29 #include "scriptstdstring/scriptstdstring.h"
30 #include "scriptmath/scriptmath.h"
31 #include "scriptany/scriptany.h"
32 #include "scriptarray/scriptarray.h"
33 #include "scripthelper/scripthelper.h"
34 // AS addons end
35 
36 #ifdef USE_CURL
37 #include <stdio.h>
38 #include <curl/curl.h>
39 //#include <curl/types.h>
40 #include <curl/easy.h>
41 #endif //USE_CURL
42 
43 #include <cfloat>
44 
45 #include "Application.h"
46 #include "Actor.h"
47 #include "ActorManager.h"
48 #include "Collisions.h"
49 #include "Console.h"
50 #include "GameContext.h"
51 #include "GameScript.h"
52 #include "LocalStorage.h"
53 #include "OgreScriptBuilder.h"
54 #include "PlatformUtils.h"
55 #include "ScriptEvents.h"
56 #include "ScriptUtils.h"
57 #include "Utils.h"
58 #include "VehicleAI.h"
59 
60 #include "InputEngine.h"
61 
62 using namespace Ogre;
63 using namespace RoR;
64 using namespace AngelScript;
65 
67 {
68  switch (c)
69  {
70  case ScriptCategory::INVALID: return "INVALID";
71  case ScriptCategory::ACTOR: return "ACTOR";
72  case ScriptCategory::TERRAIN: return "TERRAIN";
73  case ScriptCategory::CUSTOM: return "CUSTOM";
74  default: return "";
75  }
76 }
77 
78 ScriptUnit::ScriptUnit()
79 {
80  // Constructs `ActorPtr` - doesn't compile without `#include Actor.h` - not pretty if in header (even if auto-generated by C++).
81 }
82 
83 ScriptUnit::~ScriptUnit()
84 {
85  // Destructs `ActorPtr` - doesn't compile without `#include Actor.h` - not pretty if in header (even if auto-generated by C++).
86 }
87 
88 // some hacky functions
89 
90 void logString(const std::string &str)
91 {
92  App::GetScriptEngine()->SLOG(str);
93 }
94 
95 // the class implementation
96 
97 ScriptEngine::ScriptEngine() :
98  context(0)
99  , engine(0)
100  , scriptLog(0)
101 {
102  scriptLog = LogManager::getSingleton().createLog(PathCombine(App::sys_logs_dir->getStr(), "Angelscript.log"), false);
103  this->init();
104 }
105 
107 {
108  // Clean up
109  if (engine) engine->Release();
110  if (context) context->Release();
111 }
112 
113 void ScriptEngine::messageLogged( const String& message, LogMessageLevel lml, bool maskDebug, const String &logName, bool& skipThisMessage)
114 {
116 }
117 
118 // continue with initializing everything
120 {
121  SLOG("ScriptEngine initializing ...");
122  int result;
123 
124  // Create the script engine
125  engine = AngelScript::asCreateScriptEngine(ANGELSCRIPT_VERSION);
126 
127  engine->SetEngineProperty(AngelScript::asEP_ALLOW_UNSAFE_REFERENCES, true); // Needed for ImGui
128 
129  // Set the message callback to receive information on errors in human readable form.
130  // It's recommended to do this right after the creation of the engine, because if
131  // some registration fails the engine may send valuable information to the message
132  // stream.
133  result = engine->SetMessageCallback(AngelScript::asMETHOD(ScriptEngine,msgCallback), this, AngelScript::asCALL_THISCALL);
134  if (result < 0)
135  {
136  if (result == AngelScript::asINVALID_ARG)
137  {
138  SLOG("One of the arguments is incorrect, e.g. obj is null for a class method.");
139  return;
140  }
141  else if (result == AngelScript::asNOT_SUPPORTED)
142  {
143  SLOG(" The arguments are not supported, e.g. asCALL_GENERIC.");
144  return;
145  }
146  SLOG("Unkown error while setting up message callback");
147  return;
148  }
149 
150  // AngelScript doesn't have a built-in string type, as there is no definite standard
151  // string type for C++ applications. Every developer is free to register it's own string type.
152  // The SDK do however provide a standard add-on for registering a string type, so it's not
153  // necessary to register your own string type if you don't want to.
154  AngelScript::RegisterScriptArray(engine, true);
155  AngelScript::RegisterStdString(engine);
156  AngelScript::RegisterStdStringUtils(engine);
157  AngelScript::RegisterScriptMath(engine);
158  static float SCRIPT_FLT_MAX = FLT_MAX;
159  static int SCRIPT_INT_MAX = INT_MAX;
160  result = engine->RegisterGlobalProperty("const float FLT_MAX", &SCRIPT_FLT_MAX); ROR_ASSERT( result >= 0 );
161  result = engine->RegisterGlobalProperty("const int INT_MAX", &SCRIPT_INT_MAX); ROR_ASSERT(result >= 0);
162  AngelScript::RegisterScriptAny(engine);
163  AngelScript::RegisterScriptDictionary(engine);
164 
165  // some useful global functions
166  result = engine->RegisterGlobalFunction("void log(const string &in)", AngelScript::asFUNCTION(logString), AngelScript::asCALL_CDECL); ROR_ASSERT( result >= 0 );
167  result = engine->RegisterGlobalFunction("void print(const string &in)", AngelScript::asFUNCTION(logString), AngelScript::asCALL_CDECL); ROR_ASSERT( result >= 0 );
168 
169  RegisterOgreObjects(engine); // vector2/3, degree, radian, quaternion, color
170  RegisterLocalStorage(engine); // LocalStorage
171  RegisterInputEngine(engine); // InputEngineClass, inputEvents
172  RegisterImGuiBindings(engine); // ImGUi::
173  RegisterVehicleAi(engine); // VehicleAIClass, aiEvents, AiValues
174  RegisterConsole(engine); // ConsoleClass, CVarClass, CVarFlags
175  RegisterEngine(engine); // EngineClass, enum autoswitch, enum
176  RegisterActor(engine); // BeamClass
177  RegisterProceduralRoad(engine);// procedural_point, ProceduralRoadClass, ProceduralObjectClass, ProceduralManagerClass
178  RegisterTerrain(engine); // TerrainClass
179  RegisterMessageQueue(engine); // enum MsgType
180  RegisterSoundScript(engine); // SoundTriggers, ModulationSource, SoundScriptTemplate...
181  RegisterGameScript(engine); // GameScriptClass
182  RegisterScriptEvents(engine); // scriptEvents
183  RegisterGenericFileFormat(engine); // TokenType, GenericDocumentClass, GenericDocReaderClass
184  RegisterCacheSystem(engine); // LoaderType, CacheEntryClass, CacheSystemClass
185 
186  // now the global instances
187  result = engine->RegisterGlobalProperty("GameScriptClass game", &m_game_script); ROR_ASSERT(result>=0);
188  result = engine->RegisterGlobalProperty("ConsoleClass console", App::GetConsole()); ROR_ASSERT(result>=0);
189  result = engine->RegisterGlobalProperty("InputEngineClass inputs", App::GetInputEngine()); ROR_ASSERT(result>=0);
190  result = engine->RegisterGlobalProperty("CacheSystemClass modcache", App::GetCacheSystem()); ROR_ASSERT(result>=0);
191 
192  SLOG("Type registrations done. If you see no error above everything should be working");
193 
194  context = engine->CreateContext();
195 }
196 
197 void ScriptEngine::msgCallback(const AngelScript::asSMessageInfo *msg)
198 {
199  const char *type = "Error";
200  if ( msg->type == AngelScript::asMSGTYPE_INFORMATION )
201  type = "Info";
202  else if ( msg->type == AngelScript::asMSGTYPE_WARNING )
203  type = "Warning";
204 
205  char tmp[1024]="";
206  sprintf(tmp, "%s (%d, %d): %s = %s", msg->section, msg->row, msg->col, type, msg->message);
207  SLOG(tmp);
208 
210  m_currently_executing_script_unit, msg->type, msg->row, msg->col, // ints
211  msg->section, msg->message); // strings
212 }
213 
214 std::string ptr2str(const char* ptr) { if (ptr) return ptr; else return ""; }
215 
216 void ScriptEngine::lineCallback(AngelScript::asIScriptContext* ctx)
217 {
218  std::string funcName, funcObjTypeName, objName;
219  if (ctx->GetFunction())
220  {
221  funcName = ptr2str(ctx->GetFunction()->GetName());
222  objName = ptr2str(ctx->GetFunction()->GetObjectName());
223  if (ctx->GetFunction()->GetObjectType())
224  funcObjTypeName = ptr2str(ctx->GetFunction()->GetObjectType()->GetName());
225  }
227  // ints
228  m_currently_executing_script_unit, ctx->GetLineNumber(), ctx->GetCallstackSize(), 0,
229  // strings
230  funcName, funcObjTypeName, objName
231  );
232 }
233 
234 void ScriptEngine::exceptionCallback(AngelScript::asIScriptContext* ctx)
235 {
236  //Disabled, too noisy//this->logExceptionDetails();
237  std::string funcName;
238  if (ctx->GetExceptionFunction())
239  {
240  funcName = ptr2str(ctx->GetExceptionFunction()->GetName());
241  }
243  m_currently_executing_script_unit, 0, ctx->GetExceptionLineNumber(), 0, // ints
244  funcName, ctx->GetExceptionString()); // strings
245 }
246 
247 void ScriptEngine::forwardExceptionAsScriptEvent(const std::string& from)
248 {
249  // Forwards useful info from C++ exceptions to script in the form of game event.
250  // AngelScript doesn't have exceptions in the `try{}catch{}` sense
251  // (in AS jargon, 'Exception' means basically 'panic' as in Lua/Rust...)
252  // and most exceptions this game encounters (`Ogre::Exception`) are trivially recoverable,
253  // so it doesn't make sense to panic AngelScript when they happen.
254  // =======================================================================================
255 
256  try { throw; } // Rethrow
257 
258  // OGRE
259  catch (Ogre::IOException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "IOException", e.getDescription()); }
260  catch (Ogre::InvalidStateException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "InvalidStateException", e.getDescription()); }
261  catch (Ogre::InvalidParametersException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "InvalidParametersException", e.getDescription()); }
262  catch (Ogre::RenderingAPIException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "RenderingAPIException", e.getDescription()); }
263  catch (Ogre::ItemIdentityException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "ItemIdentityException", e.getDescription()); }
264  catch (Ogre::FileNotFoundException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "FileNotFoundException", e.getDescription()); }
265  catch (Ogre::InternalErrorException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "InternalErrorException", e.getDescription()); }
266  catch (Ogre::RuntimeAssertionException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "RuntimeAssertionException", e.getDescription()); }
267  catch (Ogre::UnimplementedException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "UnimplementedException", e.getDescription()); }
268  catch (Ogre::InvalidCallException& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "InvalidCallException", e.getDescription()); }
269  catch (Ogre::Exception& e ) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "Exception", e.getDescription()); }
270 
271  // STD
272  catch (std::exception& e) { TRIGGER_EVENT_ASYNC(SE_GENERIC_EXCEPTION_CAUGHT, m_currently_executing_script_unit,0,0,0, from, "std::exception", e.what()); }
273 
274  // Unrecognized
276 }
277 
279 {
280  // Helper for executing any script function/snippet;
281  // * sets LineCallback (on demand - when script registers for SE_ANGELSCRIPT_LINECALLBACK event)
282  // * sets ExceptionCallback (on demand - when script registers for SE_ANGELSCRIPT_EXCEPTIONCALLBACK event)
283  // * sets currently executed NID;
284  // IMPORTANT: The `asIScriptContext::Prepare()` must be already done (this enables programmer to set args).
285  // IMPORTANT: `m_currently_executing_event_trigger` must be set externally!
286  // =========================================================================================================
287 
288  // Automatically attach the LineCallback if the script registered for the event.
289  // (except if we're about to run that callback - that would trap us in loop)
291  && m_script_units[nid].eventMask & SE_ANGELSCRIPT_LINECALLBACK)
292  {
293  int result = context->SetLineCallback(asMETHOD(ScriptEngine, lineCallback), this, asCALL_THISCALL);
294  if (result < 0)
295  {
296  SLOG(fmt::format("Warning: Could not attach LineCallback to NID {}, error code {}; continuing without it...", result, nid));
297  }
298  }
299 
300  // Attach the ExceptionCallback if the script registered for the event.
302  {
303  int result = context->SetExceptionCallback(asMETHOD(ScriptEngine, exceptionCallback), this, asCALL_THISCALL);
304  if (result < 0)
305  {
306  SLOG(fmt::format("Warning: Could not attach ExceptionCallback to NID {}, error code {}; continuing without it...", result, nid));
307  }
308  }
309 
310  // Run the script
312  int result = context->Execute();
314 
315  if ( result != AngelScript::asEXECUTION_FINISHED )
316  {
317  // The execution didn't complete as expected. Determine what happened.
318  if ( result == AngelScript::asEXECUTION_ABORTED )
319  {
320  SLOG("The script was aborted before it could finish. Probably it timed out.");
321  }
322  else if ( result == AngelScript::asEXECUTION_EXCEPTION )
323  {
324  // An exception occurred, let the script writer know what happened so it can be corrected.
325  // NOTE: this result is only reported if exception callback is not registered, see `SetExceptionCallback()`
326  SLOG("The script ended with exception; details below:");
327  // Write some information about the script exception
328  SLOG("\tcontext.ExceptionLineNumber: " + TOSTRING(context->GetExceptionLineNumber()));
329  SLOG("\tcontext.ExceptionString: " + ptr2str(context->GetExceptionString()));
330  AngelScript::asIScriptFunction* func = context->GetExceptionFunction();
331  if (func)
332  {
333  SLOG("\tcontext.ExceptionFunction.Declaration: " + ptr2str(func->GetDeclaration()));
334  SLOG("\tcontext.ExceptionFunction.ModuleName: " + ptr2str(func->GetModuleName()));
335  SLOG("\tcontext.ExceptionFunction.ScriptSectionName: " + ptr2str(func->GetScriptSectionName()));
336  SLOG("\tcontext.ExceptionFunction.ObjectName: " + ptr2str(func->GetObjectName()));
337  }
338  }
339  else if (result == AngelScript::asCONTEXT_NOT_PREPARED)
340  {
341  if (context->GetFunction())
342  {
343  SLOG(fmt::format("The script ended with error code asCONTEXT_NOT_PREPARED; Function to execute: {},currently triggered event: {}, NID: {}",
344  context->GetFunction()->GetName(), fmt::underlying(m_currently_executing_event_trigger), nid));
345  }
346  else
347  {
348  SLOG(fmt::format("The script ended with error code asCONTEXT_NOT_PREPARED; Function to execute NOT SET,currently triggered event: {}, NID: {}",
349  fmt::underlying(m_currently_executing_event_trigger), nid));
350  }
351  }
352  else
353  {
354  SLOG("The script ended for some unforeseen reason " + TOSTRING(result));
355  }
356  }
357 
358  // Clear the callbacks so they don't intercept unrelated operations.
359  context->ClearLineCallback();
360  context->ClearExceptionCallback();
361 
362  return result;
363 }
364 
366 {
367  asIScriptFunction* scriptFunc = this->engine->GetFunctionById(asFunctionID);
368  if (!scriptFunc)
369  {
370  SLOG(fmt::format("Cannot execute script function with ID {} - not found", asFunctionID));
371  return false;
372  }
373 
374  int result = this->context->Prepare(scriptFunc);
375  if (result < 0)
376  {
377  SLOG(fmt::format("Cannot execute script function '{}': `AngelScript::Context::Prepare()` reported error code {}",
378  scriptFunc->GetName(), result));
379  return false;
380  }
381 
382  return true;
383 }
384 
386 {
387  if (!engine)
389 
390  if (!context)
392 
393  if (!this->scriptUnitExists(nid))
395 
396  out_mod = this->getScriptUnit(nid).scriptModule;
397  if (!out_mod)
399 
400  return SCRIPTRETCODE_SUCCESS;
401 }
402 
404 {
405  // Check if we need to execute any strings
406  std::vector<String> tmpQueue;
407  stringExecutionQueue.pull(tmpQueue);
408  std::vector<String>::iterator it;
409  for (it=tmpQueue.begin(); it!=tmpQueue.end();it++)
410  {
411  executeString(*it);
412  }
413 
414  // framestep stuff below
415  if (!engine || !context) return;
416 
417  for (auto& pair: m_script_units)
418  {
419  ScriptUnitID_t nid = pair.first;
420  if (m_script_units[nid].frameStepFunctionPtr)
421  {
422  // Set the function pointer and arguments
423  context->Prepare(m_script_units[nid].frameStepFunctionPtr);
424  context->SetArgFloat(0, dt);
425 
426  // Run the context via helper
428  }
429  }
430 }
431 
432 int ScriptEngine::fireEvent(std::string instanceName, float intensity)
433 {
434  if (!engine || !context)
435  return 0;
436 
437  for (auto& pair: m_script_units)
438  {
439  ScriptUnitID_t id = pair.first;
440  AngelScript::asIScriptFunction* func = m_script_units[id].scriptModule->GetFunctionByDecl(
441  "void fireEvent(string, float)"); // TODO: this shouldn't be hard coded --neorej16
442 
443  context->Prepare(func);
444 
445  // Set the function arguments
446  context->SetArgObject(0, &instanceName);
447  context->SetArgFloat (1, intensity);
448 
450  int r = context->Execute();
452  if ( r == AngelScript::asEXECUTION_FINISHED )
453  {
454  // The return value is only valid if the execution finished successfully
455  AngelScript::asDWORD ret = context->GetReturnDWord();
456  }
457  }
458 
459  return 0;
460 }
461 
462 void ScriptEngine::envokeCallback(int _functionId, eventsource_t *source, NodeNum_t nodenum, int type)
463 {
464  // THIS IS OBSOLETE - Use `eventCallbackEx()` and `SE_EVENTBOX_ENTER` instead.
465  // ################
466  // Legacy eventbox handler, specified by name in .TOBJ file or in call to `game.spawnObject()`
467  // ===========================================================================================
468  if (!engine || !context)
469  return;
470 
471  for (auto& pair: m_script_units)
472  {
473  ScriptUnitID_t id = pair.first;
474  int functionId = _functionId;
475  if (functionId <= 0 && (m_script_units[id].defaultEventCallbackFunctionPtr != nullptr))
476  {
477  // use the default event handler instead then
478  functionId = m_script_units[id].defaultEventCallbackFunctionPtr->GetId();
479  }
480 
481  if (this->prepareContextAndHandleErrors(id, functionId))
482  {
483  // Set the function arguments
484  context->SetArgDWord (0, type);
485  context->SetArgObject(1, &source->es_instance_name);
486  context->SetArgObject(2, &source->es_box_name);
487  if (nodenum != NODENUM_INVALID)
488  context->SetArgDWord (3, static_cast<AngelScript::asDWORD>(nodenum));
489  else
490  context->SetArgDWord (3, static_cast<AngelScript::asDWORD>(-1));
491 
493  }
494  }
495 }
496 
497 void ScriptEngine::queueStringForExecution(const String command)
498 {
499  stringExecutionQueue.push(command);
500 }
501 
503 {
504  int result = 0;
505  AngelScript::asIScriptModule* mod = nullptr;
506  // Only works with terrain script module (classic behavior)
507  result = this->validateScriptModule(m_terrain_script_unit, /*[out]*/ mod);
508  if (result == 0)
509  {
510  result = ExecuteString(engine, command.c_str(), mod, context);
511  if (result < 0)
512  {
513  SLOG(fmt::format("Error {} while executing string `{}`", result, command));
514  }
515  }
516  return result;
517 }
518 
519 ScriptRetCode_t ScriptEngine::addFunction(const String &arg, const ScriptUnitID_t nid /*= SCRIPTUNITID_DEFAULT*/)
520 {
521  int result = 0;
522  AngelScript::asIScriptModule* mod = nullptr;
523  result = this->validateScriptModule(nid, /*[out]*/ mod);
524  if (result == 0)
525  {
526  AngelScript::asIScriptFunction* func = nullptr;
527  result = mod->CompileFunction("addfunc", arg.c_str(), 0, AngelScript::asCOMP_ADD_TO_MODULE, &func);
528  if (result == 0)
529  {
530  // successfully added function; Check if we added a "special" function
531 
532  if (func == mod->GetFunctionByDecl("void frameStep(float)"))
533  {
534  if (m_script_units[m_terrain_script_unit].frameStepFunctionPtr == nullptr)
535  m_script_units[m_terrain_script_unit].frameStepFunctionPtr = func;
536  }
537  else if (func == mod->GetFunctionByDecl("void eventCallback(int, int)"))
538  {
539  if (m_script_units[m_terrain_script_unit].eventCallbackFunctionPtr == nullptr)
540  m_script_units[m_terrain_script_unit].eventCallbackFunctionPtr = func;
541  }
542  else if (func == mod->GetFunctionByDecl("void eventCallbackEx(scriptEvents, int, int, int, int, string, string, string, string)"))
543  {
544  if (m_script_units[m_terrain_script_unit].eventCallbackExFunctionPtr == nullptr)
545  m_script_units[m_terrain_script_unit].eventCallbackExFunctionPtr = func;
546  }
547  // THIS IS OBSOLETE - Use `eventCallbackEx()` and `SE_EVENTBOX_ENTER` instead. See commentary in `envokeCallback()`
548  else if (func == this->getFunctionByDeclAndLogCandidates(
551  {
552  if (m_script_units[m_terrain_script_unit].defaultEventCallbackFunctionPtr == nullptr)
553  m_script_units[m_terrain_script_unit].defaultEventCallbackFunctionPtr = func;
554  }
555  }
556  else
557  {
558  SLOG(fmt::format("Error {} adding function `{}` to script module '{}'", result, arg, mod->GetName()));
559  }
560  // We must release the function object
561  if (func)
562  func->Release();
563  }
564 
565  return result;
566 }
567 
568 ScriptRetCode_t ScriptEngine::functionExists(const String& arg, const ScriptUnitID_t nid /*= SCRIPTUNITID_DEFAULT*/)
569 {
570  int result = 0;
571  AngelScript::asIScriptModule* mod = nullptr;
572  result = this->validateScriptModule(nid, /*[out]*/ mod);
573  if (result == 0)
574  {
575  if (mod->GetFunctionByDecl(arg.c_str()) != nullptr)
577  }
578  return result;
579 }
580 
581 ScriptRetCode_t ScriptEngine::deleteFunction(const String &arg, const ScriptUnitID_t nid /*= SCRIPTUNITID_DEFAULT*/)
582 {
583  int result = 0;
584  AngelScript::asIScriptModule* mod = nullptr;
585  result = this->validateScriptModule(nid, /*[out]*/ mod);
586  if (result == 0)
587  {
588  AngelScript::asIScriptFunction* func = mod->GetFunctionByDecl(arg.c_str());
589  if (func != nullptr)
590  {
591  // Warning: The function is not destroyed immediately, only when no more references point to it.
592  result = mod->RemoveFunction(func);
593  if (result != 0)
594  {
595  SLOG(fmt::format("Error {} removing function `{}` from module '{}' - continuing anyway (compatibility).", result, arg, mod->GetName()));
596  }
597 
598  // Since functions can be recursive, we'll call the garbage
599  // collector to make sure the object is really freed
600  engine->GarbageCollect();
601 
602  // Check if we removed a "special" function
603 
604  if (m_script_units[m_terrain_script_unit].frameStepFunctionPtr == func)
605  m_script_units[m_terrain_script_unit].frameStepFunctionPtr = nullptr;
606 
607  if (m_script_units[m_terrain_script_unit].eventCallbackFunctionPtr == func)
608  m_script_units[m_terrain_script_unit].eventCallbackFunctionPtr = nullptr;
609 
610  if (m_script_units[m_terrain_script_unit].eventCallbackExFunctionPtr == func)
611  m_script_units[m_terrain_script_unit].eventCallbackExFunctionPtr = nullptr;
612 
613  if (m_script_units[m_terrain_script_unit].defaultEventCallbackFunctionPtr == func)
614  m_script_units[m_terrain_script_unit].defaultEventCallbackFunctionPtr = nullptr;
615  }
616  else
617  {
618  SLOG(fmt::format("Could not remove function `{}` from module '{}' - not found.", arg, mod->GetName()));
619  }
620  }
621  return result;
622 }
623 
624 ScriptRetCode_t ScriptEngine::addVariable(const String &arg, const ScriptUnitID_t nid /*= SCRIPTUNITID_DEFAULT*/)
625 {
626  int result = 0;
627  AngelScript::asIScriptModule* mod = nullptr;
628  result = this->validateScriptModule(nid, /*[out]*/ mod);
629  if (result == 0)
630  {
631  result = mod->CompileGlobalVar("addvar", arg.c_str(), 0);
632  if (result < 0)
633  {
634  SLOG(fmt::format("Error {} while adding variable `{}` to module '{}'", result, arg, mod->GetName()));
635  }
636  }
637  return result;
638 }
639 
640 ScriptRetCode_t ScriptEngine::variableExists(const String& arg, const ScriptUnitID_t nid /*= SCRIPTUNITID_DEFAULT*/)
641 {
642  int result = 0;
643  AngelScript::asIScriptModule* mod = nullptr;
644  result = this->validateScriptModule(nid, /*[out]*/ mod);
645  if (result == 0)
646  {
647  result = mod->GetGlobalVarIndexByName(arg.c_str());
648  if (result >= 0)
649  {
650  result = 0;
651  }
652  }
653  return result;
654 }
655 
656 ScriptRetCode_t ScriptEngine::deleteVariable(const String &arg, const ScriptUnitID_t nid /*= SCRIPTUNITID_DEFAULT*/)
657 {
658  int result = 0;
659  AngelScript::asIScriptModule* mod = nullptr;
660  result = this->validateScriptModule(nid, /*[out]*/ mod);
661  if (result == 0)
662  {
663  result = mod->GetGlobalVarIndexByName(arg.c_str());
664  if (result >= 0)
665  {
666  result = mod->RemoveGlobalVar(result);
667  }
668  }
669  if (result < 0)
670  {
671  SLOG(fmt::format("Error {} while removing variable `{}` from module '{}'", result, arg, mod->GetName()));
672  }
673  return result;
674 }
675 
676 ScriptRetCode_t ScriptEngine::getVariable(const Ogre::String& varName, void *ref, int refTypeId, const ScriptUnitID_t nid /*= SCRIPTUNITID_DEFAULT*/)
677 {
678  AngelScript::asIScriptModule* mod = nullptr;
679  int modResult = this->validateScriptModule(nid, /*[out]*/ mod);
680  if (modResult < 0)
681  return modResult;
682 
683  int index = mod->GetGlobalVarIndexByName(varName.c_str());
684  if (index < 0)
685  {
686  return index;
687  }
688 
689  const char* asVarName = nullptr;
690  const char* asNamespace = nullptr;
691  int asTypeId = 0;
692  bool asConst = false;
693  int getResult = mod->GetGlobalVar(index, &asVarName, &asNamespace, &asTypeId, &asConst);
694  if (getResult < 0)
695  {
696  return getResult;
697  }
698 
699  LOG(fmt::format("[RoR|Scripting] getScriptVariable() - '{}' global var info: name='{}', namespace='{}', typeid={}, const={}",
700  varName, asVarName, asNamespace, asTypeId, asConst));
701 
702  // ~~ DEV NOTE: The following code is adopted from AngelScript's add-on 'scriptany.cpp', function `Retrieve()` ~~
703 
704  if( refTypeId & asTYPEID_OBJHANDLE )
705  {
706  // Is the handle type compatible with the stored value?
707 
708  // A handle can be retrieved if the stored type is a handle of same or compatible type
709  // or if the stored type is an object that implements the interface that the handle refer to.
710  if( (asTypeId & asTYPEID_MASK_OBJECT) )
711  {
712  // Don't allow the retrieval if the stored handle is to a const object but not the wanted handle
713  if( (asTypeId & asTYPEID_HANDLETOCONST) && !(refTypeId & asTYPEID_HANDLETOCONST) )
714  {
715  SLOG(fmt::format("Error in `getScriptVariable()` - '{}' is a handle to `const` object but the requested type is not.", varName));
717  }
718 
719  // RefCastObject will increment the refCount of the returned pointer if successful
720  engine->RefCastObject(mod->GetAddressOfGlobalVar(index), engine->GetTypeInfoById(asTypeId), engine->GetTypeInfoById(refTypeId), reinterpret_cast<void**>(ref));
721  if( *(asPWORD*)ref == 0 )
722  {
723  SLOG(fmt::format("Error in `getScriptVariable()` - '{}': reference-cast from '{}' to '{}' yielded null",
724  varName, engine->GetTypeDeclaration(asTypeId), engine->GetTypeDeclaration(refTypeId)));
726  }
727  return 0;
728  }
729  }
730  else if( refTypeId & asTYPEID_MASK_OBJECT )
731  {
732  // Is the object type compatible with the stored value?
733 
734  // Copy the object into the given reference
735  if( asTypeId == refTypeId )
736  {
737  engine->AssignScriptObject(ref, mod->GetAddressOfGlobalVar(index), engine->GetTypeInfoById(asTypeId));
738  return 0;
739  }
740  }
741  else
742  {
743  // Is the primitive type _IDENTICAL TO_ the stored value?
744  // NOTE: implicit conversions are not done automatically, we would have to write code for each case separately
745 
746  if( asTypeId == refTypeId )
747  {
748  int size = engine->GetSizeOfPrimitiveType(refTypeId);
749  memcpy(ref, mod->GetAddressOfGlobalVar(index), size);
750  return 0;
751  }
752  }
753 
754  SLOG(fmt::format("Error in `getScriptVariable()` - '{}' has incompatible type, expected '{}' (typeid {}), got '{}' (typeid {})",
755  varName, engine->GetTypeDeclaration(refTypeId), refTypeId, engine->GetTypeDeclaration(asTypeId), asTypeId));
757 }
758 
759 asIScriptFunction* ScriptEngine::getFunctionByDeclAndLogCandidates(ScriptUnitID_t nid, GetFuncFlags_t flags, const std::string& funcName, const std::string& fmtFuncDecl)
760 {
761  std::string decl = fmt::format(fmtFuncDecl, funcName);
762  asIScriptFunction* retval = m_script_units[nid].scriptModule->GetFunctionByDecl(decl.c_str());
763  if (!retval)
764  {
765  asIScriptFunction* candidate = m_script_units[nid].scriptModule->GetFunctionByName(funcName.c_str());
766  if (candidate && BITMASK_IS_0(flags, GETFUNCFLAG_SILENT))
767  {
768  SLOG(fmt::format("Warning: a callback function with signature '{}' was not found"
769  " but a function with given name exists: '{}' - did you make a typo in arguments?",
770  decl, candidate->GetDeclaration()));
771  }
772  else if (!candidate && BITMASK_IS_1(flags, GETFUNCFLAG_REQUIRED))
773  {
774  SLOG(fmt::format("Warning: a callback function with signature '{}' was not found",
775  decl));
776  }
777  }
778  return retval;
779 }
780 
781 void ScriptEngine::triggerEvent(scriptEvents eventnum, int arg1, int arg2ex, int arg3ex, int arg4ex, std::string arg5ex, std::string arg6ex, std::string arg7ex, std::string arg8ex)
782 {
783  if (!engine || !context || !m_events_enabled) return;
784 
785  for (auto& pair: m_script_units)
786  {
787  ScriptUnitID_t id = pair.first;
788  asIScriptFunction* callback = m_script_units[id].eventCallbackExFunctionPtr;
789  if (!callback)
790  callback = m_script_units[id].eventCallbackFunctionPtr;
791  if (!callback)
792  continue;
793 
794  if (m_script_units[id].eventMask & eventnum)
795  {
796  // script registered for that event, so sent it
797  if (this->prepareContextAndHandleErrors(id, callback->GetId()))
798  {
799 
800  // Set the function arguments
801  context->SetArgDWord(0, eventnum);
802  context->SetArgDWord(1, arg1);
803  if (callback == m_script_units[id].eventCallbackExFunctionPtr)
804  {
805  // Extended arguments
806  context->SetArgDWord(2, arg2ex);
807  context->SetArgDWord(3, arg3ex);
808  context->SetArgDWord(4, arg4ex);
809  context->SetArgObject(5, &arg5ex);
810  context->SetArgObject(6, &arg6ex);
811  context->SetArgObject(7, &arg7ex);
812  context->SetArgObject(8, &arg8ex);
813  }
814  }
815 
819  }
820  }
821 }
822 
823 String ScriptEngine::composeModuleName(String const& scriptName, ScriptCategory origin, ScriptUnitID_t id)
824 {
825  return fmt::format("{}(category:{},unique ID:{})", scriptName, ScriptCategoryToString(origin), id);
826 }
827 
829  String scriptOrGadgetFileName, ScriptCategory category/* = ScriptCategory::TERRAIN*/,
830  ActorPtr associatedActor /*= nullptr*/, std::string buffer /* =""*/)
831 {
832  // This function creates a new script unit, tries to set it up and removes it if setup fails.
833  // -----------------------------------------------------------------------------------------
834  // A script unit is how Rigs of Rods organizes scripts from various sources.
835  // Because the script is executed during loading, it's wrapping unit must
836  // be created early, and removed if setup fails.
837  static ScriptUnitID_t id_counter = 0;
838 
839  std::string basename, ext, scriptName;
840  Ogre::StringUtil::splitBaseFilename(scriptOrGadgetFileName, basename, ext);
841  CacheEntryPtr originatingGadget;
842  if (ext == "gadget")
843  {
844  originatingGadget = App::GetCacheSystem()->FindEntryByFilename(LT_Gadget, /* partial: */false, scriptOrGadgetFileName);
845  if (!originatingGadget)
846  {
848  fmt::format("Could not load script '{}' - gadget not found.", scriptOrGadgetFileName));
849  return SCRIPTUNITID_INVALID;
850  }
851  App::GetCacheSystem()->LoadResource(originatingGadget);
852  scriptName = fmt::format("{}.as", basename);
853  // Ensure a .gadget file is always loaded as `GADGET`, even if requested as `CUSTOM`
854  category = ScriptCategory::GADGET;
855  }
856  else if (ext == "as")
857  {
858  scriptName = scriptOrGadgetFileName;
859  }
860  else
861  {
863  fmt::format("Could not load script '{}' - unknown file extension.", scriptOrGadgetFileName));
864  return SCRIPTUNITID_INVALID;
865  }
866 
867  ScriptUnitID_t unit_id = id_counter++;
868  auto itor_pair = m_script_units.insert(std::make_pair(unit_id, ScriptUnit()));
869  m_script_units[unit_id].uniqueId = unit_id;
870  m_script_units[unit_id].scriptName = scriptName;
871  m_script_units[unit_id].scriptCategory = category;
872  m_script_units[unit_id].scriptBuffer = buffer;
873  m_script_units[unit_id].originatingGadget = originatingGadget;
874  if (category == ScriptCategory::TERRAIN)
875  {
876  m_terrain_script_unit = unit_id;
877  }
878  else if (category == ScriptCategory::ACTOR)
879  {
880  m_script_units[unit_id].associatedActor = associatedActor;
881  }
882 
883  // Perform the actual script loading, building and running main().
884  int result = this->setupScriptUnit(unit_id);
885 
886  // Regardless of result, add to recent script list. Running scripts are filtered out when displaying.
887  if (category == ScriptCategory::CUSTOM && buffer == "")
888  {
890  }
891  else if (category == ScriptCategory::GADGET && originatingGadget)
892  {
893  CvarAddFileToList(App::app_recent_scripts, originatingGadget->fname);
894  }
895 
896  // If setup failed, remove the unit.
897  if (result != 0)
898  {
899  m_script_units.erase(itor_pair.first);
900  if (category == ScriptCategory::TERRAIN)
901  {
903  }
904  return SCRIPTUNITID_INVALID;
905  }
906 
907  return unit_id;
908 }
909 
911 {
912  int result=0;
913 
914  String moduleName = this->composeModuleName(
915  m_script_units[unit_id].scriptName, m_script_units[unit_id].scriptCategory, m_script_units[unit_id].uniqueId);
916 
917  // The builder is a helper class that will load the script file,
918  // search for #include directives, and load any included files as
919  // well.
920  OgreScriptBuilder builder;
921 
922  // A script module is how AngelScript organizes scripts.
923  // It contains the script loaded by user plus all `#include`-d scripts.
924  result = builder.StartNewModule(engine, moduleName.c_str());
925  if ( result < 0 )
926  {
928  fmt::format("Could not load script '{}' - failed to create module.", moduleName));
929  return result;
930  }
931  m_script_units[unit_id].scriptModule = engine->GetModule(moduleName.c_str(), AngelScript::asGM_ONLY_IF_EXISTS);
932 
933  // For actor scripts, add global var `thisActor` to the module
934  if (m_script_units[unit_id].scriptCategory == ScriptCategory::ACTOR)
935  {
936  result = m_script_units[unit_id].scriptModule->AddScriptSection(m_script_units[unit_id].scriptName.c_str(), "BeamClass@ thisActor;");
937  if (result < 0)
938  {
940  fmt::format("Could not load script '{}' - failed to create global variable `thisActor`.", moduleName));
941  return result;
942  }
943  }
944 
945  // add global var `thisScript` to the module (initialized in place).
946  result = m_script_units[unit_id].scriptModule->AddScriptSection(m_script_units[unit_id].scriptName.c_str(),
947  fmt::format("const int thisScript = {};", unit_id).c_str());
948  if (result < 0)
949  {
951  fmt::format("Could not load script '{}' - failed to create global variable `thisScript`.", moduleName));
952  return result;
953  }
954 
955  // If buffer is non-empty, load from memory; otherwise from filesystem as usual.
956  if (m_script_units[unit_id].scriptBuffer != "")
957  {
958  result = builder.AddSectionFromMemory(m_script_units[unit_id].scriptName.c_str(), m_script_units[unit_id].scriptBuffer.c_str());
959  if (result < 0)
960  {
962  fmt::format("Could not load script '{}' from buffer", moduleName));
963  return result;
964  }
965  }
966  else
967  {
968  // Load the script from the file system.
969  result = builder.AddSectionFromFile(m_script_units[unit_id].scriptName.c_str());
970  if ( result < 0 )
971  {
973  fmt::format("Could not load script '{}' - failed to process file.", moduleName));
974  return result;
975  }
976  }
977 
978  // Build the AngelScript module - this loads `#include`-d scripts
979  // and runs any global statements, for example constructors of
980  // global objects like raceManager in 'races.as'. For this reason,
981  // the game must already be aware of the script, but only temporarily.
982  m_currently_executing_script_unit = unit_id; // for `BuildModule()` below.
983  result = builder.BuildModule();
985  if ( result < 0 )
986  {
988  fmt::format("Could not load script '{}' - failed to build module. See 'Angelscript.log' for more info.", moduleName));
989  return result;
990  }
991 
992  String scriptHash;
993  if (m_script_units[unit_id].scriptCategory == ScriptCategory::TERRAIN) // Classic behavior
994  scriptHash = builder.GetHash();
995 
996  // get some other optional functions
997  m_script_units[unit_id].frameStepFunctionPtr = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void frameStep(float)");
998 
999  m_script_units[unit_id].eventCallbackFunctionPtr = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void eventCallback(int, int)");
1000  m_script_units[unit_id].eventCallbackExFunctionPtr = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void eventCallbackEx(scriptEvents, int, int, int, int, string, string, string, string)");
1001 
1002  // THIS IS OBSOLETE - Use `eventCallbackEx()` and `SE_EVENTBOX_ENTER` instead. See commentary in `envokeCallback()`
1003  m_script_units[unit_id].defaultEventCallbackFunctionPtr = this->getFunctionByDeclAndLogCandidates(
1005 
1006  // Find the function that is to be called.
1007  auto main_func = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void main()");
1008  if ( main_func == nullptr )
1009  {
1010  // The function couldn't be found. Continue without invoking it - other callbacks like `frameStep()` can still run.
1011  return 0;
1012  }
1013 
1014  // Prepare the script context with the function we wish to execute. Prepare()
1015  // must be called on the context before each new script function that will be
1016  // executed. Note, that if you intend to execute the same function several
1017  // times, it might be a good idea to store the function id returned by
1018  // GetFunctionIDByDecl(), so that this relatively slow call can be skipped.
1019  result = context->Prepare(main_func);
1020  if (result < 0)
1021  {
1023  fmt::format("Could not load script '{}' - failed to build module.", moduleName));
1024  context->Release();
1025  return -1;
1026  }
1027 
1028  // For actor scripts, initialize the global var `thisActor`
1029  if (m_script_units[unit_id].scriptCategory == ScriptCategory::ACTOR)
1030  {
1031  int var_index = m_script_units[unit_id].scriptModule->GetGlobalVarIndexByName("thisActor");
1032  if (var_index < 0)
1033  {
1034  SLOG("Could not find global var `thisActor`");
1035  return -1;
1036  }
1037 
1038  // Example: https://www.gamedev.net/forums/topic/644188-angelscript-2263-global-property-issues-solved/5069638/
1039  Actor** thisActorAddr = (Actor**)m_script_units[unit_id].scriptModule->GetAddressOfGlobalVar(var_index);
1040  if (thisActorAddr == nullptr)
1041  {
1042  SLOG("Could not retrieve address of global var `thisActor`");
1043  return -1;
1044  }
1045  *thisActorAddr = m_script_units[unit_id].associatedActor.GetRef();
1046  (*thisActorAddr)->AddRef();
1047  }
1048 
1049  // Execute the `main()` function in the script.
1050  // The function must have full access to the game API.
1051  SLOG(fmt::format("Executing main() in {}", moduleName));
1052  int mainfunc_result = this->executeContextAndHandleErrors(unit_id);
1053  if ( mainfunc_result != AngelScript::asEXECUTION_FINISHED )
1054  {
1056  fmt::format("Could not load script '{}' - error running function `main()`, check AngelScript.log", moduleName));
1057  }
1058  else
1059  {
1060  SLOG("The script finished successfully.");
1061  }
1062 
1063  return mainfunc_result;
1064 }
1065 
1067 {
1068  if (this->scriptUnitExists(nid))
1069  {
1070  if (m_script_units[nid].scriptModule != nullptr)
1071  {
1072  engine->DiscardModule(m_script_units[nid].scriptModule->GetName());
1073  m_script_units[nid].scriptModule = nullptr;
1074  }
1075  m_script_units.erase(nid);
1076  }
1077 
1078  if (m_terrain_script_unit == nid)
1079  {
1081  }
1082 }
1083 
1085 {
1086  // Always remove right away, to avoid attaching twice
1087  scriptLog->removeListener(this);
1088 
1089  // Re-attach if requested
1090  if (doForward)
1091  {
1092  scriptLog->addListener(this);
1093  }
1094 }
1095 
1097 {
1098  return nid != SCRIPTUNITID_INVALID
1099  && m_script_units.find(nid) != m_script_units.end();
1100 }
1101 
1103 {
1104  ROR_ASSERT(this->scriptUnitExists(nid));
1105  return m_script_units[nid];
1106 }
RoR::ScriptUnit
Represents a loaded script and all associated resources/handles.
Definition: ScriptEngine.h:70
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
GameContext.h
Game state manager and message-queue provider.
RoR::ScriptEngine::unloadScript
void unloadScript(ScriptUnitID_t unique_id)
Unloads a script.
Definition: ScriptEngine.cpp:1066
RoR::ScriptEngine::msgCallback
void msgCallback(const AngelScript::asSMessageInfo *msg)
Optional (but very recommended!) callback providing diagnostic info when things fail to start (most n...
Definition: ScriptEngine.cpp:197
RoR::SCRIPTRETCODE_ENGINE_NOT_CREATED
@ SCRIPTRETCODE_ENGINE_NOT_CREATED
Definition: ScriptEngine.h:164
RoR::ScriptEngine::exceptionCallback
void exceptionCallback(AngelScript::asIScriptContext *ctx)
Optional callback invoked when the script critically fails, allowing debugging.
Definition: ScriptEngine.cpp:234
RoR::ScriptEngine
This class represents the angelscript scripting interface.
Definition: ScriptEngine.h:175
RoR::RegisterOgreObjects
void RegisterOgreObjects(AngelScript::asIScriptEngine *engine)
defined in OgreAngelscript.cpp
Definition: OgreAngelscript.cpp:683
RoR::Actor
Softbody object; can be anything from soda can to a space shuttle Constructed from a truck definition...
Definition: Actor.h:50
RoR::ScriptEngine::scriptUnitExists
bool scriptUnitExists(ScriptUnitID_t unique_id)
Definition: ScriptEngine.cpp:1096
OgreScriptBuilder
Definition: OgreScriptBuilder.h:39
RoR::RegisterLocalStorage
void RegisterLocalStorage(AngelScript::asIScriptEngine *engine)
Registers RoR::LocalStorage, defined in LocalStorageAngelscript.cpp.
RoR::GETFUNCFLAG_OPTIONAL
const GetFuncFlags_t GETFUNCFLAG_OPTIONAL
Only logs warning if candidate is found, to help modder find a typo.
Definition: ScriptEngine.h:109
RoR::RegisterVehicleAi
void RegisterVehicleAi(AngelScript::asIScriptEngine *engine)
defined in VehicleAiAngelscript.cpp
RoR::SCRIPTUNITID_INVALID
static const ScriptUnitID_t SCRIPTUNITID_INVALID
Definition: ForwardDeclarations.h:42
VehicleAI.h
Simple waypoint AI.
OgreScriptBuilder::GetHash
Ogre::String GetHash()
Definition: OgreScriptBuilder.h:42
RoR::RegisterCacheSystem
void RegisterCacheSystem(AngelScript::asIScriptEngine *engine)
defined in CacheSystemAngelscript.cpp
RoR::RegisterConsole
void RegisterConsole(AngelScript::asIScriptEngine *engine)
Registers RoR::Console, defined in ConsoleAngelscript.cpp.
RoR::eventsource_t::es_instance_name
std::string es_instance_name
Specified by user when calling "GameScript::spawnObject()".
Definition: Collisions.h:42
RoR::ScriptEngine::deleteVariable
ScriptRetCode_t deleteVariable(const Ogre::String &arg, const ScriptUnitID_t nid=SCRIPTUNITID_DEFAULT)
Deletes a global variable from the script.
Definition: ScriptEngine.cpp:656
RoR::ScriptEngine::setupScriptUnit
int setupScriptUnit(int unit_id)
Helper for loadScript(), does the actual building without worry about unit management.
Definition: ScriptEngine.cpp:910
RoR::eventsource_t
< Scripting
Definition: Collisions.h:40
RoR::SE_ANGELSCRIPT_LINECALLBACK
@ SE_ANGELSCRIPT_LINECALLBACK
The diagnostic info directly from AngelScript engine (see SetLineCallback()), args: #1 ScriptUnitID,...
Definition: ScriptEvents.h:56
RoR::NODENUM_INVALID
static const NodeNum_t NODENUM_INVALID
Definition: ForwardDeclarations.h:55
format
Truck file format(technical spec)
RoR::ScriptRetCode_t
int ScriptRetCode_t
see enum RoR::ScriptRetCode - combines AngelScript codes and RoR internal codes.
Definition: ForwardDeclarations.h:82
RoR::ScriptEngine::composeModuleName
Ogre::String composeModuleName(Ogre::String const &scriptName, ScriptCategory origin, ScriptUnitID_t id)
Packs name + important info to one string, for logging and reporting purposes.
Definition: ScriptEngine.cpp:823
RoR::ScriptEngine::addFunction
ScriptRetCode_t addFunction(const Ogre::String &arg, const ScriptUnitID_t nid=SCRIPTUNITID_DEFAULT)
Adds a global function to the script.
Definition: ScriptEngine.cpp:519
RoR::TRIGGER_EVENT_ASYNC
void TRIGGER_EVENT_ASYNC(scriptEvents type, int arg1, int arg2ex=0, int arg3ex=0, int arg4ex=0, std::string arg5ex="", std::string arg6ex="", std::string arg7ex="", std::string arg8ex="")
Asynchronously (via MSG_SIM_SCRIPT_EVENT_TRIGGERED) invoke script function eventCallbackEx(),...
Definition: ScriptEngine.h:51
OgreScriptBuilder.h
RoR::ScriptCategoryToString
const char * ScriptCategoryToString(ScriptCategory c)
Definition: ScriptEngine.cpp:66
Console.h
RoR::Console::putMessage
void putMessage(MessageArea area, MessageType type, std::string const &msg, std::string icon="")
Definition: Console.cpp:103
RoR::ScriptEngine::setForwardScriptLogToConsole
void setForwardScriptLogToConsole(bool doForward)
Definition: ScriptEngine.cpp:1084
RoR::ScriptEngine::forwardExceptionAsScriptEvent
void forwardExceptionAsScriptEvent(const std::string &from)
Forwards useful info from C++ try{}catch{} exceptions to script in the form of game event.
Definition: ScriptEngine.cpp:247
RoR::GetFuncFlags_t
BitMask_t GetFuncFlags_t
Flags for RoR::ScriptEngine::getFunctionByDeclAndLogCandidates()
Definition: ScriptEngine.h:108
RoR::ScriptEngine::queueStringForExecution
void queueStringForExecution(const Ogre::String command)
Queues a string for execution.
Definition: ScriptEngine.cpp:497
RoR::App::sys_logs_dir
CVar * sys_logs_dir
Definition: Application.cpp:167
Utils.h
RoR::ScriptEngine::framestep
void framestep(Ogre::Real dt)
Calls the script's framestep function to be able to use timed things inside the script.
Definition: ScriptEngine.cpp:403
RefCountingObjectPtr< Actor >
RoR::Console::CONSOLE_SYSTEM_ERROR
@ CONSOLE_SYSTEM_ERROR
Definition: Console.h:52
ActorManager.h
Actor.h
RoR::App::GetScriptEngine
ScriptEngine * GetScriptEngine()
Definition: Application.cpp:283
RoR::ScriptEngine::getFunctionByDeclAndLogCandidates
AngelScript::asIScriptFunction * getFunctionByDeclAndLogCandidates(ScriptUnitID_t nid, GetFuncFlags_t flags, const std::string &funcName, const std::string &fmtFuncDecl)
Finds a function by full declaration, and if not found, finds candidates by name and logs them to Ang...
Definition: ScriptEngine.cpp:759
RoR::GETFUNC_DEFAULTEVENTCALLBACK_NAME
const std::string GETFUNC_DEFAULTEVENTCALLBACK_NAME
Definition: ScriptEngine.h:115
RoR::ScriptEngine::prepareContextAndHandleErrors
bool prepareContextAndHandleErrors(ScriptUnitID_t nid, int asFunctionID)
Helper for executing any script function/snippet; does asIScriptContext::Prepare() and reports any er...
Definition: ScriptEngine.cpp:365
RoR::SCRIPTRETCODE_SCRIPTUNIT_NO_MODULE
@ SCRIPTRETCODE_SCRIPTUNIT_NO_MODULE
Definition: ScriptEngine.h:167
RoR::RegisterTerrain
void RegisterTerrain(AngelScript::asIScriptEngine *engine)
Registers RoR::Terrain, defined in TerrainAngelscript.cpp.
RoR::RegisterProceduralRoad
void RegisterProceduralRoad(AngelScript::asIScriptEngine *engine)
defined in ProceduralRoadAngelscript.cpp
TOSTRING
#define TOSTRING(x)
Definition: Application.h:56
BITMASK_IS_0
#define BITMASK_IS_0(VAR, FLAGS)
Definition: BitFlags.h:13
RoR::RegisterGameScript
void RegisterGameScript(AngelScript::asIScriptEngine *engine)
Registers RoR::GameScript, defined in GameScriptAngelscript.cpp.
RoR::ScriptEngine::init
void init()
This function initialzies the engine and registeres all types.
Definition: ScriptEngine.cpp:119
RoR::NodeNum_t
uint16_t NodeNum_t
Node position within Actor::ar_nodes; use RoR::NODENUM_INVALID as empty value.
Definition: ForwardDeclarations.h:54
RoR::ScriptEngine::variableExists
ScriptRetCode_t variableExists(const Ogre::String &arg, const ScriptUnitID_t nid=SCRIPTUNITID_DEFAULT)
Adds a global variable to the script.
Definition: ScriptEngine.cpp:640
RoR::CacheSystem::FindEntryByFilename
CacheEntryPtr FindEntryByFilename(RoR::LoaderType type, bool partial, const std::string &_filename_maybe_bundlequalified)
Returns NULL if none found; "Bundle-qualified" format also specifies the ZIP/directory in modcache,...
Definition: CacheSystem.cpp:186
RoR::eventsource_t::es_box_name
std::string es_box_name
Specified in ODEF file as "event".
Definition: Collisions.h:43
RoR::PathCombine
std::string PathCombine(std::string a, std::string b)
Definition: PlatformUtils.h:48
id_counter
static int id_counter
Definition: ProceduralRoad.cpp:36
RoR::RegisterGenericFileFormat
void RegisterGenericFileFormat(AngelScript::asIScriptEngine *engine)
defined in GenericFileFormatAngelscript.cpp
RoR::RegisterScriptEvents
void RegisterScriptEvents(AngelScript::asIScriptEngine *engine)
Registers enum scriptEvents, defined in ScriptEventsAngelscript.cpp.
BITMASK_IS_1
#define BITMASK_IS_1(VAR, FLAGS)
Definition: BitFlags.h:14
RoR::ScriptEngine::executeContextAndHandleErrors
int executeContextAndHandleErrors(ScriptUnitID_t nid)
Helper for executing any script function/snippet; registers Line/Exception callbacks (on demand) and ...
Definition: ScriptEngine.cpp:278
RoR::RegisterActor
void RegisterActor(AngelScript::asIScriptEngine *engine)
defined in ActorAngelscript.cpp
ScriptEngine.h
RoR::Console::forwardLogMessage
void forwardLogMessage(MessageArea area, std::string const &msg, Ogre::LogMessageLevel lml)
Definition: Console.cpp:40
RoR::ScriptEngine::envokeCallback
void envokeCallback(int functionId, eventsource_t *source, NodeNum_t nodenum=NODENUM_INVALID, int type=0)
Definition: ScriptEngine.cpp:462
PlatformUtils.h
Platform-specific utilities. We use narrow UTF-8 encoded strings as paths. Inspired by http://utf8eve...
RoR::LT_Gadget
@ LT_Gadget
Definition: Application.h:325
RoR::SE_NO_EVENTS
@ SE_NO_EVENTS
Definition: ScriptEvents.h:67
RoR::ScriptEngine::lineCallback
void lineCallback(AngelScript::asIScriptContext *ctx)
Optional callback which receives diagnostic info for every executed statement.
Definition: ScriptEngine.cpp:216
RoR::ScriptEngine::~ScriptEngine
~ScriptEngine()
Definition: ScriptEngine.cpp:106
RoR::SE_GENERIC_EXCEPTION_CAUGHT
@ SE_GENERIC_EXCEPTION_CAUGHT
Triggered when C++ exception (usually Ogre::Exception) is thrown; #1 ScriptUnitID,...
Definition: ScriptEvents.h:61
logString
void logString(const std::string &str)
Definition: ScriptEngine.cpp:90
RoR::ScriptEngine::scriptLog
Ogre::Log * scriptLog
Definition: ScriptEngine.h:370
Application.h
Central state/object manager and communications hub.
RoR::App::GetConsole
Console * GetConsole()
Definition: Application.cpp:274
RoR::SE_ANGELSCRIPT_MSGCALLBACK
@ SE_ANGELSCRIPT_MSGCALLBACK
The diagnostic info directly from AngelScript engine (see asSMessageInfo), args: #1 ScriptUnitID,...
Definition: ScriptEvents.h:55
RoR::ScriptEngine::m_currently_executing_script_unit
ScriptUnitID_t m_currently_executing_script_unit
Definition: ScriptEngine.h:374
GameScript.h
RoR::SCRIPTRETCODE_SCRIPTUNIT_NOT_EXISTS
@ SCRIPTRETCODE_SCRIPTUNIT_NOT_EXISTS
Definition: ScriptEngine.h:166
RoR::ScriptEngine::m_currently_executing_event_trigger
scriptEvents m_currently_executing_event_trigger
Definition: ScriptEngine.h:375
RoR::ScriptEngine::m_events_enabled
bool m_events_enabled
Hack to enable fast shutdown without cleanup.
Definition: ScriptEngine.h:376
RoR::SCRIPTRETCODE_UNSPECIFIED_ERROR
@ SCRIPTRETCODE_UNSPECIFIED_ERROR
Definition: ScriptEngine.h:163
RoR::ScriptEngine::triggerEvent
void triggerEvent(scriptEvents eventnum, int arg1=0, int arg2ex=0, int arg3ex=0, int arg4ex=0, std::string arg5ex="", std::string arg6ex="", std::string arg7ex="", std::string arg8ex="")
triggers an event; Not to be used by the end-user.
Definition: ScriptEngine.cpp:781
RoR::RegisterEngine
void RegisterEngine(AngelScript::asIScriptEngine *engine)
Register class Engine and related enums, defined in EngineAngelscript.cpp.
ptr2str
std::string ptr2str(const char *ptr)
Definition: ScriptEngine.cpp:214
RoR::ScriptEngine::engine
AngelScript::asIScriptEngine * engine
instance of the scripting engine
Definition: ScriptEngine.h:368
RoR::CvarAddFileToList
void CvarAddFileToList(CVar *cvar, const std::string &filename)
Definition: Utils.cpp:206
RoR::ScriptCategory::TERRAIN
@ TERRAIN
Defined in terrn2 file under '[Scripts]', receives terrain eventbox notifications.
LocalStorage.h
RoR::GETFUNC_DEFAULTEVENTCALLBACK_SIGFMT
const std::string GETFUNC_DEFAULTEVENTCALLBACK_SIGFMT
Definition: ScriptEngine.h:114
RoR::App::GetCacheSystem
CacheSystem * GetCacheSystem()
Definition: Application.cpp:276
RoR::ScriptEngine::fireEvent
int fireEvent(std::string instanceName, float intensity)
Definition: ScriptEngine.cpp:432
RoR::ScriptEngine::getVariable
ScriptRetCode_t getVariable(const Ogre::String &varName, void *ref, int typeID, ScriptUnitID_t nid=SCRIPTUNITID_DEFAULT)
Retrieves a global variable from any running script.
Definition: ScriptEngine.cpp:676
RoR::App::app_recent_scripts
CVar * app_recent_scripts
Definition: Application.cpp:93
RoR::RegisterImGuiBindings
void RegisterImGuiBindings(AngelScript::asIScriptEngine *engine)
defined in ImGuiAngelscript.cpp
Definition: ImGuiAngelscript.cpp:53
RoR::GETFUNCFLAG_REQUIRED
const GetFuncFlags_t GETFUNCFLAG_REQUIRED
Always logs warning that function was not found.
Definition: ScriptEngine.h:110
RoR::RegisterMessageQueue
void RegisterMessageQueue(AngelScript::asIScriptEngine *engine)
Registers enum MsgType, defined in MsgQueueAngelscript.cpp.
InterThreadStoreVector::push
void push(T v)
Definition: InterThreadStoreVector.h:33
RoR::CacheSystem::LoadResource
void LoadResource(CacheEntryPtr &t)
Loads the associated resource bundle if not already done.
Definition: CacheSystem.cpp:1539
RoR::ScriptEngine::addVariable
ScriptRetCode_t addVariable(const Ogre::String &arg, const ScriptUnitID_t nid=SCRIPTUNITID_DEFAULT)
Adds a global variable to the script.
Definition: ScriptEngine.cpp:624
RoR::SCRIPTRETCODE_FUNCTION_NOT_EXISTS
@ SCRIPTRETCODE_FUNCTION_NOT_EXISTS
Definition: ScriptEngine.h:168
RoR::RegisterSoundScript
void RegisterSoundScript(AngelScript::asIScriptEngine *engine)
defined in SoundScriptAngelscript.cpp
Definition: SoundScriptAngelscript.cpp:30
RoR::ScriptEngine::m_terrain_script_unit
ScriptUnitID_t m_terrain_script_unit
Definition: ScriptEngine.h:373
RoR::ScriptEngine::getScriptUnit
ScriptUnit & getScriptUnit(ScriptUnitID_t unique_id)
Definition: ScriptEngine.cpp:1102
RoR::ScriptEngine::validateScriptModule
ScriptRetCode_t validateScriptModule(const ScriptUnitID_t nid, AngelScript::asIScriptModule *&out_mod)
Helper for all manipulations with functions/variables; ensures the script unit exists and is fully se...
Definition: ScriptEngine.cpp:385
RoR::RegisterInputEngine
void RegisterInputEngine(AngelScript::asIScriptEngine *engine)
Registers RoR::InputEngine, defined in InputEngineAngelscript.cpp.
RoR::ScriptEngine::context
AngelScript::asIScriptContext * context
context in which all scripting happens
Definition: ScriptEngine.h:369
ScriptEvents.h
RoR::ScriptEngine::m_script_units
ScriptUnitMap m_script_units
Definition: ScriptEngine.h:372
RoR::App::GetInputEngine
InputEngine * GetInputEngine()
Definition: Application.cpp:275
RoR::ScriptCategory::ACTOR
@ ACTOR
Defined in truck file under 'scripts', contains global variable BeamClass@ thisActor.
RoR::ScriptEngine::functionExists
ScriptRetCode_t functionExists(const Ogre::String &arg, const ScriptUnitID_t nid=SCRIPTUNITID_DEFAULT)
Checks if a global function exists.
Definition: ScriptEngine.cpp:568
RoR::ScriptEngine::m_game_script
GameScript m_game_script
Definition: ScriptEngine.h:371
RoR::ScriptEngine::stringExecutionQueue
InterThreadStoreVector< Ogre::String > stringExecutionQueue
The string execution queue.
Definition: ScriptEngine.h:378
InputEngine.h
Handles controller inputs from player. Defines input events and binding mechanism,...
Ogre
Definition: ExtinguishableFireAffector.cpp:35
ScriptUtils.h
RoR::ScriptEngine::loadScript
ScriptUnitID_t loadScript(Ogre::String filename, ScriptCategory category=ScriptCategory::TERRAIN, ActorPtr associatedActor=nullptr, std::string buffer="")
Loads a script.
Definition: ScriptEngine.cpp:828
RoR::ScriptUnitID_t
int ScriptUnitID_t
Unique sequentially generated ID of a loaded and running scriptin session. Use ScriptEngine::getScrip...
Definition: ForwardDeclarations.h:41
RoR::Console::CONSOLE_MSGTYPE_INFO
@ CONSOLE_MSGTYPE_INFO
Generic message.
Definition: Console.h:60
RoR::ScriptCategory::GADGET
@ GADGET
Associated with a .gadget mod file, launched via UI or any method given below for CUSTOM scripts (use...
RoR::Console::CONSOLE_MSGTYPE_SCRIPT
@ CONSOLE_MSGTYPE_SCRIPT
Messages sent from scripts.
Definition: Console.h:62
RoR::ScriptCategory
ScriptCategory
Note: Either of these can be loaded from script using game.pushMessage(MSG_APP_LOAD_SCRIPT_REQUESTED....
Definition: ScriptEngine.h:58
RoR::SCRIPTRETCODE_CONTEXT_NOT_CREATED
@ SCRIPTRETCODE_CONTEXT_NOT_CREATED
Definition: ScriptEngine.h:165
Collisions.h
RoR::ScriptEngine::executeString
ScriptRetCode_t executeString(Ogre::String command)
executes a string (useful for the console)
Definition: ScriptEngine.cpp:502
RoR::ScriptUnit::scriptModule
AngelScript::asIScriptModule * scriptModule
Definition: ScriptEngine.h:78
RoR::scriptEvents
scriptEvents
This enum describes what events are existing. The script can register to receive events.
Definition: ScriptEvents.h:30
RoR::SCRIPTRETCODE_SUCCESS
@ SCRIPTRETCODE_SUCCESS
Definition: ScriptEngine.h:121
RoR::SE_ANGELSCRIPT_EXCEPTIONCALLBACK
@ SE_ANGELSCRIPT_EXCEPTIONCALLBACK
The diagnostic info directly from AngelScript engine (see SetExceptionCallback()),...
Definition: ScriptEvents.h:57
RoR
Definition: AppContext.h:36
RoR::ScriptEngine::deleteFunction
ScriptRetCode_t deleteFunction(const Ogre::String &arg, const ScriptUnitID_t nid=SCRIPTUNITID_DEFAULT)
Deletes a global function from the script.
Definition: ScriptEngine.cpp:581
RoR::ScriptCategory::CUSTOM
@ CUSTOM
Loaded by user via either: A) ingame console 'loadscript'; B) RoR.cfg 'app_custom_scripts'; C) comman...
InterThreadStoreVector::pull
void pull(std::vector< T > &res)
Definition: InterThreadStoreVector.h:39
RoR::CacheEntry::fname
Ogre::String fname
filename
Definition: CacheSystem.h:67
RoR::GETFUNCFLAG_SILENT
const GetFuncFlags_t GETFUNCFLAG_SILENT
Never logs.
Definition: ScriptEngine.h:111
RoR::ScriptEngine::messageLogged
void messageLogged(const Ogre::String &message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String &logName, bool &skipThisMessage)
Definition: ScriptEngine.cpp:113