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