RigsofRods
Soft-body Physics Simulation
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 scriptName, 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  ScriptUnitID_t unit_id = id_counter++;
840  auto itor_pair = m_script_units.insert(std::make_pair(unit_id, ScriptUnit()));
841  m_script_units[unit_id].uniqueId = unit_id;
842  m_script_units[unit_id].scriptName = scriptName;
843  m_script_units[unit_id].scriptCategory = category;
844  m_script_units[unit_id].scriptBuffer = buffer;
845  if (category == ScriptCategory::TERRAIN)
846  {
847  m_terrain_script_unit = unit_id;
848  }
849  else if (category == ScriptCategory::ACTOR)
850  {
851  m_script_units[unit_id].associatedActor = associatedActor;
852  }
853 
854  // Perform the actual script loading, building and running main().
855  int result = this->setupScriptUnit(unit_id);
856 
857  // Regardless of result, add to recent script list. Running scripts are filtered out when displaying.
858  if (category == ScriptCategory::CUSTOM && buffer == "")
859  {
861  }
862 
863  // If setup failed, remove the unit.
864  if (result != 0)
865  {
866  m_script_units.erase(itor_pair.first);
867  if (category == ScriptCategory::TERRAIN)
868  {
870  }
871  return SCRIPTUNITID_INVALID;
872  }
873 
874  return unit_id;
875 }
876 
878 {
879  int result=0;
880 
881  String moduleName = this->composeModuleName(
882  m_script_units[unit_id].scriptName, m_script_units[unit_id].scriptCategory, m_script_units[unit_id].uniqueId);
883 
884  // The builder is a helper class that will load the script file,
885  // search for #include directives, and load any included files as
886  // well.
887  OgreScriptBuilder builder;
888 
889  // A script module is how AngelScript organizes scripts.
890  // It contains the script loaded by user plus all `#include`-d scripts.
891  result = builder.StartNewModule(engine, moduleName.c_str());
892  if ( result < 0 )
893  {
895  fmt::format("Could not load script '{}' - failed to create module.", moduleName));
896  return result;
897  }
898  m_script_units[unit_id].scriptModule = engine->GetModule(moduleName.c_str(), AngelScript::asGM_ONLY_IF_EXISTS);
899 
900  // For actor scripts, add global var `thisActor` to the module
901  if (m_script_units[unit_id].scriptCategory == ScriptCategory::ACTOR)
902  {
903  result = m_script_units[unit_id].scriptModule->AddScriptSection(m_script_units[unit_id].scriptName.c_str(), "BeamClass@ thisActor;");
904  if (result < 0)
905  {
907  fmt::format("Could not load script '{}' - failed to create global variable `thisActor`.", moduleName));
908  return result;
909  }
910  }
911 
912  // add global var `thisScript` to the module (initialized in place).
913  result = m_script_units[unit_id].scriptModule->AddScriptSection(m_script_units[unit_id].scriptName.c_str(),
914  fmt::format("const int thisScript = {};", unit_id).c_str());
915  if (result < 0)
916  {
918  fmt::format("Could not load script '{}' - failed to create global variable `thisScript`.", moduleName));
919  return result;
920  }
921 
922  // If buffer is non-empty, load from memory; otherwise from filesystem as usual.
923  if (m_script_units[unit_id].scriptBuffer != "")
924  {
925  result = builder.AddSectionFromMemory(m_script_units[unit_id].scriptName.c_str(), m_script_units[unit_id].scriptBuffer.c_str());
926  if (result < 0)
927  {
929  fmt::format("Could not load script '{}' from buffer", moduleName));
930  return result;
931  }
932  }
933  else
934  {
935  // Load the script from the file system.
936  result = builder.AddSectionFromFile(m_script_units[unit_id].scriptName.c_str());
937  if ( result < 0 )
938  {
940  fmt::format("Could not load script '{}' - failed to process file.", moduleName));
941  return result;
942  }
943  }
944 
945  // Build the AngelScript module - this loads `#include`-d scripts
946  // and runs any global statements, for example constructors of
947  // global objects like raceManager in 'races.as'. For this reason,
948  // the game must already be aware of the script, but only temporarily.
949  m_currently_executing_script_unit = unit_id; // for `BuildModule()` below.
950  result = builder.BuildModule();
952  if ( result < 0 )
953  {
955  fmt::format("Could not load script '{}' - failed to build module. See 'Angelscript.log' for more info.", moduleName));
956  return result;
957  }
958 
959  String scriptHash;
960  if (m_script_units[unit_id].scriptCategory == ScriptCategory::TERRAIN) // Classic behavior
961  scriptHash = builder.GetHash();
962 
963  // get some other optional functions
964  m_script_units[unit_id].frameStepFunctionPtr = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void frameStep(float)");
965 
966  m_script_units[unit_id].eventCallbackFunctionPtr = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void eventCallback(int, int)");
967  m_script_units[unit_id].eventCallbackExFunctionPtr = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void eventCallbackEx(scriptEvents, int, int, int, int, string, string, string, string)");
968 
969  // THIS IS OBSOLETE - Use `eventCallbackEx()` and `SE_EVENTBOX_ENTER` instead. See commentary in `envokeCallback()`
970  m_script_units[unit_id].defaultEventCallbackFunctionPtr = this->getFunctionByDeclAndLogCandidates(
972 
973  // Find the function that is to be called.
974  auto main_func = m_script_units[unit_id].scriptModule->GetFunctionByDecl("void main()");
975  if ( main_func == nullptr )
976  {
977  // The function couldn't be found. Continue without invoking it - other callbacks like `frameStep()` can still run.
978  return 0;
979  }
980 
981  // Prepare the script context with the function we wish to execute. Prepare()
982  // must be called on the context before each new script function that will be
983  // executed. Note, that if you intend to execute the same function several
984  // times, it might be a good idea to store the function id returned by
985  // GetFunctionIDByDecl(), so that this relatively slow call can be skipped.
986  result = context->Prepare(main_func);
987  if (result < 0)
988  {
990  fmt::format("Could not load script '{}' - failed to build module.", moduleName));
991  context->Release();
992  return -1;
993  }
994 
995  // For actor scripts, initialize the global var `thisActor`
996  if (m_script_units[unit_id].scriptCategory == ScriptCategory::ACTOR)
997  {
998  int var_index = m_script_units[unit_id].scriptModule->GetGlobalVarIndexByName("thisActor");
999  if (var_index < 0)
1000  {
1001  SLOG("Could not find global var `thisActor`");
1002  return -1;
1003  }
1004 
1005  // Example: https://www.gamedev.net/forums/topic/644188-angelscript-2263-global-property-issues-solved/5069638/
1006  Actor** thisActorAddr = (Actor**)m_script_units[unit_id].scriptModule->GetAddressOfGlobalVar(var_index);
1007  if (thisActorAddr == nullptr)
1008  {
1009  SLOG("Could not retrieve address of global var `thisActor`");
1010  return -1;
1011  }
1012  *thisActorAddr = m_script_units[unit_id].associatedActor.GetRef();
1013  (*thisActorAddr)->AddRef();
1014  }
1015 
1016  // Execute the `main()` function in the script.
1017  // The function must have full access to the game API.
1018  SLOG(fmt::format("Executing main() in {}", moduleName));
1019  int mainfunc_result = this->executeContextAndHandleErrors(unit_id);
1020  if ( mainfunc_result != AngelScript::asEXECUTION_FINISHED )
1021  {
1023  fmt::format("Could not load script '{}' - error running function `main()`, check AngelScript.log", moduleName));
1024  }
1025  else
1026  {
1027  SLOG("The script finished successfully.");
1028  }
1029 
1030  return mainfunc_result;
1031 }
1032 
1034 {
1035  if (this->scriptUnitExists(nid))
1036  {
1037  if (m_script_units[nid].scriptModule != nullptr)
1038  {
1039  engine->DiscardModule(m_script_units[nid].scriptModule->GetName());
1040  m_script_units[nid].scriptModule = nullptr;
1041  }
1042  m_script_units.erase(nid);
1043  }
1044 
1045  if (m_terrain_script_unit == nid)
1046  {
1048  }
1049 }
1050 
1052 {
1053  // Always remove right away, to avoid attaching twice
1054  scriptLog->removeListener(this);
1055 
1056  // Re-attach if requested
1057  if (doForward)
1058  {
1059  scriptLog->addListener(this);
1060  }
1061 }
1062 
1064 {
1065  return nid != SCRIPTUNITID_INVALID
1066  && m_script_units.find(nid) != m_script_units.end();
1067 }
1068 
1070 {
1071  ROR_ASSERT(this->scriptUnitExists(nid));
1072  return m_script_units[nid];
1073 }
RoR::ScriptUnit
Represents a loaded script and all associated resources/handles.
Definition: ScriptEngine.h:69
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:1033
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:162
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:173
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:1063
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:107
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:877
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:1051
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:106
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:282
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:113
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:165
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::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::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:368
Application.h
Central state/object manager and communications hub.
RoR::App::GetConsole
Console * GetConsole()
Definition: Application.cpp:273
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:372
GameScript.h
RoR::SCRIPTRETCODE_SCRIPTUNIT_NOT_EXISTS
@ SCRIPTRETCODE_SCRIPTUNIT_NOT_EXISTS
Definition: ScriptEngine.h:164
RoR::ScriptEngine::m_currently_executing_event_trigger
scriptEvents m_currently_executing_event_trigger
Definition: ScriptEngine.h:373
RoR::ScriptEngine::m_events_enabled
bool m_events_enabled
Hack to enable fast shutdown without cleanup.
Definition: ScriptEngine.h:374
RoR::SCRIPTRETCODE_UNSPECIFIED_ERROR
@ SCRIPTRETCODE_UNSPECIFIED_ERROR
Definition: ScriptEngine.h:161
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:366
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.
RoR::ScriptEngine::loadScript
ScriptUnitID_t loadScript(Ogre::String scriptname, ScriptCategory category=ScriptCategory::TERRAIN, ActorPtr associatedActor=nullptr, std::string buffer="")
Loads a script.
Definition: ScriptEngine.cpp:828
LocalStorage.h
RoR::GETFUNC_DEFAULTEVENTCALLBACK_SIGFMT
const std::string GETFUNC_DEFAULTEVENTCALLBACK_SIGFMT
Definition: ScriptEngine.h:112
RoR::App::GetCacheSystem
CacheSystem * GetCacheSystem()
Definition: Application.cpp:275
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:108
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::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:166
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:371
RoR::ScriptEngine::getScriptUnit
ScriptUnit & getScriptUnit(ScriptUnitID_t unique_id)
Definition: ScriptEngine.cpp:1069
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:367
ScriptEvents.h
RoR::ScriptEngine::m_script_units
ScriptUnitMap m_script_units
Definition: ScriptEngine.h:370
RoR::App::GetInputEngine
InputEngine * GetInputEngine()
Definition: Application.cpp:274
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:369
RoR::ScriptEngine::stringExecutionQueue
InterThreadStoreVector< Ogre::String > stringExecutionQueue
The string execution queue.
Definition: ScriptEngine.h:376
InputEngine.h
Handles controller inputs from player. Defines input events and binding mechanism,...
Ogre
Definition: ExtinguishableFireAffector.cpp:35
ScriptUtils.h
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::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:163
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:77
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:119
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::GETFUNCFLAG_SILENT
const GetFuncFlags_t GETFUNCFLAG_SILENT
Never logs.
Definition: ScriptEngine.h:109
RoR::ScriptEngine::messageLogged
void messageLogged(const Ogre::String &message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String &logName, bool &skipThisMessage)
Definition: ScriptEngine.cpp:113