Leviathan  0.8.0.0
Leviathan game engine
Leviathan::ScriptExecutor Class Reference

Handles ScriptModule creation and AngelScript code execution. More...

#include <ScriptExecutor.h>

Public Member Functions

DLLEXPORT ScriptExecutor ()
 
DLLEXPORT ~ScriptExecutor ()
 
DLLEXPORT std::weak_ptr< ScriptModuleCreateNewModule (const std::string &name, const std::string &source, const int &modulesid=IDFactory::GetID())
 
DLLEXPORT void DeleteModule (ScriptModule *ptrtomatch)
 
DLLEXPORT bool DeleteModuleIfNoExternalReferences (int ID)
 
DLLEXPORT std::weak_ptr< ScriptModuleGetModule (const int &ID)
 
DLLEXPORT std::weak_ptr< ScriptModuleGetModuleByAngelScriptName (const char *nameofmodule)
 
template<typename ReturnT , class... Args>
ScriptRunResult< ReturnT > RunScript (const std::shared_ptr< ScriptModule > &module, ScriptRunningSetup &parameters, Args &&... args)
 Runs a function in a script. More...
 
template<typename ReturnT , class... Args>
ScriptRunResult< ReturnT > RunScript (asIScriptFunction *func, std::shared_ptr< ScriptModule > module, ScriptRunningSetup &parameters, Args &&... args)
 Runs a function in a script (that is known already) More...
 
template<typename ReturnT , class... Args>
ScriptRunResult< ReturnT > RunScriptMethod (ScriptRunningSetup &parameters, asIScriptFunction *func, void *obj, Args &&... args)
 Runs a method in a script. More...
 
DLLEXPORT std::unique_ptr< CustomScriptRunPrepareCustomScriptRun (asIScriptFunction *func, ScriptRunningSetup extraoptions=ScriptRunningSetup())
 Starts a script run that supports custom argument passing. More...
 
template<typename ReturnT >
ScriptRunResult< ReturnT > ExecuteCustomRun (const std::unique_ptr< CustomScriptRun > &run)
 Ends a custom script run by actually executing the script and returning a value. More...
 
DLLEXPORT void RunReleaseRefOnObject (void *obj, int objid)
 Finds release ref behaviour on object and calls it. More...
 
DLLEXPORT std::shared_ptr< ScriptModuleGetScriptModuleByFunction (asIScriptFunction *func, bool reporterror)
 Returns module in which script function was defined in. More...
 
DLLEXPORT asIScriptFunction * GetFunctionFromModule (ScriptModule *module, ScriptRunningSetup &parameters)
 Finds a script function in module matching setup. More...
 
DLLEXPORT int ResolveStringToASID (const char *str) const
 Converts a string to angelscript type id. Returns -1 on error. More...
 
DLLEXPORT asITypeInfo * GetTypeInfo (int type) const
 Returns an asITypeInfo object for type id or null. More...
 
DLLEXPORT asITypeInfo * GetTypeInfoByDecl (const char *str) const
 Returns an asITypeInfo object for type name or null. More...
 
DLLEXPORT asIScriptEngine * GetASEngine ()
 
DLLEXPORT void CollectGarbage ()
 Does a full garbage collection cycle. More...
 

Static Public Member Functions

static DLLEXPORT void PrintExceptionInfo (asIScriptContext *ctx, LErrorReporter &output, asIScriptFunction *func=nullptr, ScriptModule *scrptmodule=nullptr)
 Prints exception info and stacktrace to a logger. More...
 
static DLLEXPORT void PrintCallstack (asIScriptContext *ctx, LErrorReporter &output)
 
static DLLEXPORT ScriptExecutorGet ()
 

Protected Member Functions

DLLEXPORT asIScriptContext * _GetContextForExecution ()
 Called when a context is required for script execution. More...
 
DLLEXPORT void _DoneWithContext (asIScriptContext *context)
 Called after a script has been executed and the context is no longer needed. More...
 

Friends

asIScriptContext * RequestContextCallback (asIScriptEngine *engine, void *userdata)
 
void ReturnContextCallback (asIScriptEngine *engine, asIScriptContext *context, void *userdata)
 

Detailed Description

Handles ScriptModule creation and AngelScript code execution.

Definition at line 56 of file ScriptExecutor.h.

Constructor & Destructor Documentation

◆ ScriptExecutor()

ScriptExecutor::ScriptExecutor ( )

Definition at line 111 of file ScriptExecutor.cpp.

111  : engine(nullptr), AllocatedScriptModules()
112 {
113 
114  instance = this;
115 
116  // Initialize AngelScript //
117  engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
118  if(engine == nullptr) {
119 
120  Logger::Get()->Error("ScriptExecutor: Init: asCreateScriptEngine failed");
121  Logger::Get()->Info("ScriptExecutor: tried to init angelscript version " +
122  Convert::ToString(ANGELSCRIPT_VERSION));
123  Logger::Get()->Write("Did you use a wrong angelscript version? copy header files to "
124  "leviathan/Angelscript/include from your angelscript.zip");
125  throw Exception("Failed to init angelscript");
126  }
127 
128  // set callback to error report function //
129  engine->SetMessageCallback(asFUNCTION(ScriptMessageCallback), 0, asCALL_CDECL);
130 
131  // The ScriptExecutor can be retrieved from asIScriptEngine user data
132  engine->SetUserData(this);
133 
134 #ifdef ANGELSCRIPT_HAS_TRANSLATE_CALLBACK
135  // Set error translation callback
136  engine->SetTranslateAppExceptionCallback(
137  asFUNCTION(ScriptTranslateExceptionCallback), nullptr, asCALL_CDECL);
138 #endif // ANGELSCRIPT_HAS_TRANSLATE_CALLBACK
139 
140  // Context pool usage callbacks
141  engine->SetContextCallbacks(RequestContextCallback, ReturnContextCallback, this);
142 
143 
144  // Builtins are in this access group //
145  const auto initialMask =
146  engine->SetDefaultAccessMask(static_cast<AccessFlags>(ScriptAccess::Builtin));
147 
148  // math functions //
149  RegisterScriptMath(engine);
150  RegisterScriptMathComplex(engine);
151 
152  // register script string type //
153  RegisterStdString(engine);
154  RegisterScriptArray(engine, true);
155  // register other script extensions //
156  RegisterStdStringUtils(engine);
157 
158 
159  RegisterScriptDateTime(engine);
160 
161  // register dictionary object //
162  RegisterScriptDictionary(engine);
163 
164  // Register the grid addon //
165  RegisterScriptGrid(engine);
166 
167  // Register reference handles //
168  RegisterScriptHandle(engine);
169 
170  RegisterScriptWeakRef(engine);
171 
172  RegisterScriptAny(engine);
173 
174  // Put the extended standard stuff also in Builtin access mask
175 
176  if(!BindStandardFunctions(engine))
177  throw Exception("BindStandardFunctions failed");
178 
179  // All normal engine stuff is in the DefaultEngine access mask //
180  engine->SetDefaultAccessMask(static_cast<AccessFlags>(ScriptAccess::DefaultEngine));
181 
182  if(!BindOgre(engine))
183  throw Exception("BindOgre failed");
184 
185  if(!BindTypes(engine))
186  throw Exception("BindTypes failed");
187 
188  if(!BindPhysics(engine))
189  throw Exception("BindNewton failed");
190 
191  if(!BindEngineCommon(engine))
192  throw Exception("BindEngineCommon failed");
193 
194  if(!BindGUI(engine))
195  throw Exception("BindGUI failed");
196 
197  if(!BindEntity(engine))
198  throw Exception("BindEntity failed");
199 
200  // Bind notifiers //
201  if(!RegisterNotifiersWithAngelScript(engine)) {
202  // failed //
203  LOG_ERROR("ScriptExecutor: Init: AngelScript: register Notifier types failed");
204  throw Exception("Script bind failed");
205  }
206 
207  // Restore the default mask to let the application do what it wants with the masks
208  engine->SetDefaultAccessMask(initialMask);
209 
210  // bind application specific //
211  auto leviathanengine = Engine::GetEngine();
212 
213  if(leviathanengine) {
214 
215  if(!leviathanengine->GetOwningApplication()->InitLoadCustomScriptTypes(engine)) {
216 
217  LOG_ERROR("ScriptExecutor: Init: AngelScript: application register failed");
218  throw Exception("Script bind failed");
219  }
220  }
221 
222  // Verify void type //
223  const auto actualVoid = engine->GetTypeIdByDecl("void");
224  if(actualVoid != ANGELSCRIPT_VOID_TYPEID) {
225 
226  LOG_FATAL("ScriptExecutor: angelscript void type has changed! expected " +
227  std::to_string(ANGELSCRIPT_VOID_TYPEID) +
228  " (constexpr) == " + std::to_string(actualVoid) + " (actual value)");
229  }
230 }
Access to the builtin types like string and math functions.
DLLEXPORT void Write(const std::string &data) override
Definition: Logger.cpp:113
friend void ReturnContextCallback(asIScriptEngine *engine, asIScriptContext *context, void *userdata)
DLLEXPORT void Info(const std::string &data) override
Definition: Logger.cpp:164
#define LOG_ERROR(x)
Definition: Define.h:83
#define LOG_FATAL(x)
Definition: Define.h:85
constexpr auto ANGELSCRIPT_VOID_TYPEID
This has to be constant (and luckily so far it has been)
bool RegisterNotifiersWithAngelScript(asIScriptEngine *engine)
bool BindEngineCommon(asIScriptEngine *engine)
Base class for all exceptions thrown by Leviathan.
Definition: Exceptions.h:10
bool BindStandardFunctions(asIScriptEngine *engine)
Binds standard and other utilities like: std::min, std::max etc.
bool BindTypes(asIScriptEngine *engine)
Definition: TypesBind.cpp:813
Default mask for engine classes (some things are accessible even with this off)
static std::string ToString(const T &val)
Definition: Convert.h:72
bool BindOgre(asIScriptEngine *engine)
Definition: OgreBind.cpp:845
static DLLEXPORT Logger * Get()
Definition: Logger.cpp:106
bool BindGUI(asIScriptEngine *engine)
void ScriptMessageCallback(const asSMessageInfo *msg, void *param)
static DLLEXPORT Engine * GetEngine()
Definition: Engine.cpp:79
bool BindPhysics(asIScriptEngine *engine)
friend asIScriptContext * RequestContextCallback(asIScriptEngine *engine, void *userdata)
bool BindEntity(asIScriptEngine *engine)
Definition: EntityBind.cpp:521
DLLEXPORT void Error(const std::string &data) override
Definition: Logger.cpp:177

◆ ~ScriptExecutor()

ScriptExecutor::~ScriptExecutor ( )

Definition at line 231 of file ScriptExecutor.cpp.

232 {
233  {
234  Lock lock(ModulesLock);
235  auto end = AllocatedScriptModules.end();
236  for(auto iter = AllocatedScriptModules.begin(); iter != end; ++iter) {
237 
238  (*iter)->Release();
239  }
240 
241  // release/delete all modules //
242  AllocatedScriptModules.clear();
243  }
244 
245  // Release all context objects
246  for(asIScriptContext* context : ContextPool) {
247  context->Release();
248  }
249 
250  ContextPool.clear();
251 
252  // release AngelScript //
253  if(engine) {
254 
255  engine->Release();
256  engine = nullptr;
257  }
258 
259  instance = nullptr;
260 }
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

Member Function Documentation

◆ _DoneWithContext()

DLLEXPORT void Leviathan::ScriptExecutor::_DoneWithContext ( asIScriptContext *  context)
protected

Called after a script has been executed and the context is no longer needed.

Note
Also called from CustomScriptRun

Definition at line 482 of file ScriptExecutor.cpp.

483 {
484  Lock guard(ContextPoolLock);
485 
486  // Only keep 1000 contexts at most
487  if(ContextPool.size() < 1000) {
488 
489  ContextPool.push_back(context);
490  context->Unprepare();
491 
492  } else {
493 
494  context->Release();
495  }
496 }
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

◆ _GetContextForExecution()

DLLEXPORT asIScriptContext * Leviathan::ScriptExecutor::_GetContextForExecution ( )
protected

Called when a context is required for script execution.

Todo:
Allow recursive calls

Definition at line 457 of file ScriptExecutor.cpp.

458 {
459  // TODO: check for recursive call
460 
461  Lock guard(ContextPoolLock);
462 
463  // Get from pool if possible
464  if(!ContextPool.empty()) {
465  auto* ptr = ContextPool.back();
466  ContextPool.pop_back();
467  return ptr;
468  }
469 
470  // Needs more contexts
471  asIScriptContext* scriptContext = engine->CreateContext();
472 
473  if(!scriptContext) {
474 
475  LOG_ERROR("ScriptExecutor: _GetContextForExecution: Failed to create a new context");
476  return nullptr;
477  }
478 
479  return scriptContext;
480 }
#define LOG_ERROR(x)
Definition: Define.h:83
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

◆ CollectGarbage()

DLLEXPORT void ScriptExecutor::CollectGarbage ( )

Does a full garbage collection cycle.

Definition at line 692 of file ScriptExecutor.cpp.

693 {
694  engine->GarbageCollect(asGC_FULL_CYCLE);
695 }

◆ CreateNewModule()

DLLEXPORT std::weak_ptr< ScriptModule > Leviathan::ScriptExecutor::CreateNewModule ( const std::string &  name,
const std::string &  source,
const int &  modulesid = IDFactory::GetID() 
)

Definition at line 528 of file ScriptExecutor.cpp.

531 {
532  // create new module to a smart pointer //
533  auto tmpptr = std::make_shared<ScriptModule>(engine, name, modulesid, source);
534 
535  // add to vector and return //
536  Lock lock(ModulesLock);
537  AllocatedScriptModules.push_back(tmpptr);
538  return tmpptr;
539 }
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

◆ DeleteModule()

DLLEXPORT void Leviathan::ScriptExecutor::DeleteModule ( ScriptModule ptrtomatch)

Definition at line 541 of file ScriptExecutor.cpp.

542 {
543 
544  Lock lock(ModulesLock);
545 
546  // find module based on pointer and remove //
547  for(size_t i = 0; i < AllocatedScriptModules.size(); i++) {
548  if(AllocatedScriptModules[i].get() == ptrtomatch) {
549 
550  AllocatedScriptModules[i]->Release();
551  // remove //
552  AllocatedScriptModules.erase(AllocatedScriptModules.begin() + i);
553  return;
554  }
555  }
556 }
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

◆ DeleteModuleIfNoExternalReferences()

DLLEXPORT bool Leviathan::ScriptExecutor::DeleteModuleIfNoExternalReferences ( int  ID)

Definition at line 558 of file ScriptExecutor.cpp.

559 {
560 
561  Lock lock(ModulesLock);
562 
563  // Find based on the id //
564  for(size_t i = 0; i < AllocatedScriptModules.size(); i++) {
565  if(AllocatedScriptModules[i]->GetID() == ID) {
566  // Check reference count //
567  if(AllocatedScriptModules[i].use_count() != 1) {
568  // Other references exist //
569  return false;
570  }
571 
572  AllocatedScriptModules[i]->Release();
573 
574  // remove //
575  AllocatedScriptModules.erase(AllocatedScriptModules.begin() + i);
576  return true;
577  }
578  }
579  // Nothing found //
580  return false;
581 }
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

◆ ExecuteCustomRun()

template<typename ReturnT >
ScriptRunResult<ReturnT> Leviathan::ScriptExecutor::ExecuteCustomRun ( const std::unique_ptr< CustomScriptRun > &  run)
inline

Ends a custom script run by actually executing the script and returning a value.

Note
You MUST manually pass arguments before calling this! Use RunScript if you don't need custom argument passing

Definition at line 221 of file ScriptExecutor.h.

222  {
223  if(!run)
224  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
225 
226  // Run the script //
227  // TODO: timeout and debugging registering with linecallbacks here //
228  int retcode = run->Context->Execute();
229 
230  // Get the return value //
231  auto returnvalue = _HandleEndedScriptExecution<ReturnT>(
232  retcode, run->Context, run->Setup, run->Func, run->Module.get());
233 
234  // Release the context //
235  _DoneWithContext(run->Context);
236  run->Context = nullptr;
237 
238  // Return the returned value //
239  return returnvalue;
240  }
DLLEXPORT void _DoneWithContext(asIScriptContext *context)
Called after a script has been executed and the context is no longer needed.

◆ Get()

DLLEXPORT ScriptExecutor * Leviathan::ScriptExecutor::Get ( )
static
Note
Alternative way to get ScriptExecutor is from asIScriptEngine::GetUserData

Definition at line 262 of file ScriptExecutor.cpp.

263 {
264  return instance;
265 }

◆ GetASEngine()

DLLEXPORT asIScriptEngine* Leviathan::ScriptExecutor::GetASEngine ( )
inline

Definition at line 274 of file ScriptExecutor.h.

275  {
276  return engine;
277  }

◆ GetFunctionFromModule()

DLLEXPORT asIScriptFunction * ScriptExecutor::GetFunctionFromModule ( ScriptModule module,
ScriptRunningSetup parameters 
)

Finds a script function in module matching setup.

Returns
The function or null if not found or module is invalid

Sets the function existed in the parameters

Definition at line 269 of file ScriptExecutor.cpp.

271 {
272  if(!module) {
273 
274  if(parameters.PrintErrors) {
275  Logger::Get()->Error("ScriptExecutor: GetFunctionFromModule: module is nullptr");
276  }
277 
278  return nullptr;
279  }
280 
281  asIScriptFunction* func;
282 
283  asIScriptModule* asModule = module->GetModule();
284 
285  if(!asModule) {
286 
287  if(parameters.PrintErrors) {
288  Logger::Get()->Error(
289  "ScriptExecutor: GetFunctionFromModule: cannot get function from "
290  "an invalid script module: " +
291  module->GetInfoString());
292  }
293 
294  return nullptr;
295  }
296 
297  // Get the entry function from the module //
298  if(!parameters.FullDeclaration) {
299 
300  func = asModule->GetFunctionByName(parameters.Entryfunction.c_str());
301 
302  } else {
303 
304  func = asModule->GetFunctionByDecl(parameters.Entryfunction.c_str());
305  }
306 
307  if(!_CheckScriptFunctionPtr(func, parameters, module)) {
308 
309  return nullptr;
310  }
311 
312  return func;
313 }
DLLEXPORT asIScriptModule * GetModule(Lock &guard)
Builds the script if applicable.
static DLLEXPORT Logger * Get()
Definition: Logger.cpp:106
DLLEXPORT void Error(const std::string &data) override
Definition: Logger.cpp:177
DLLEXPORT std::string GetInfoString()

◆ GetModule()

DLLEXPORT std::weak_ptr< ScriptModule > Leviathan::ScriptExecutor::GetModule ( const int &  ID)

Definition at line 498 of file ScriptExecutor.cpp.

499 {
500  // loop modules and return a ptr to matching id //
501  Lock lock(ModulesLock);
502 
503  for(size_t i = 0; i < AllocatedScriptModules.size(); i++) {
504  if(AllocatedScriptModules[i]->GetID() == ID)
505  return AllocatedScriptModules[i];
506  }
507 
508  return std::shared_ptr<ScriptModule>(nullptr);
509 }
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

◆ GetModuleByAngelScriptName()

DLLEXPORT std::weak_ptr< ScriptModule > Leviathan::ScriptExecutor::GetModuleByAngelScriptName ( const char *  nameofmodule)

Definition at line 511 of file ScriptExecutor.cpp.

513 {
514  // Find a matching name //
515  std::string module(nameofmodule);
516 
517  Lock lock(ModulesLock);
518 
519  // TODO: check could this be checked by comparing pointers
520  for(size_t i = 0; i < AllocatedScriptModules.size(); i++) {
521  if(AllocatedScriptModules[i]->GetModuleName() == module)
522  return AllocatedScriptModules[i];
523  }
524 
525  return std::shared_ptr<ScriptModule>(nullptr);
526 }
std::unique_lock< std::mutex > Lock
Definition: ThreadSafe.h:16

◆ GetScriptModuleByFunction()

DLLEXPORT std::shared_ptr< ScriptModule > ScriptExecutor::GetScriptModuleByFunction ( asIScriptFunction *  func,
bool  reporterror 
)

Returns module in which script function was defined in.

Todo:
When module is null find the module by AngelScript module pointer (set the userdata pointer on the module to point to the ScriptModule object) for faster finding

Definition at line 383 of file ScriptExecutor.cpp.

385 {
386  const char* nameStr = func->GetModuleName();
387 
388  if(!nameStr || strlen(nameStr) <= 1)
389  return nullptr;
390 
391  // static_cast<ScriptModule*>(func->GetModule()->GetUserData());
392  // then find by pointer in valid script modules
393 
394  std::shared_ptr<ScriptModule> module =
395  GetModuleByAngelScriptName(func->GetModuleName()).lock();
396 
397  if(!module) {
398 
399  if(reporterror) {
400  LOG_WARNING("ScriptExecutor: GetScriptModuleByFunction: the module is no longer "
401  "available: " +
402  std::string(func->GetModuleName()));
403  }
404  }
405 
406  return module;
407 }
DLLEXPORT std::weak_ptr< ScriptModule > GetModuleByAngelScriptName(const char *nameofmodule)
#define LOG_WARNING(x)
Definition: Define.h:82

◆ GetTypeInfo()

DLLEXPORT asITypeInfo * ScriptExecutor::GetTypeInfo ( int  type) const

Returns an asITypeInfo object for type id or null.

Definition at line 683 of file ScriptExecutor.cpp.

684 {
685 
686  if(type < 0)
687  return nullptr;
688 
689  return engine->GetTypeInfoById(type);
690 }

◆ GetTypeInfoByDecl()

DLLEXPORT asITypeInfo* Leviathan::ScriptExecutor::GetTypeInfoByDecl ( const char *  str) const

Returns an asITypeInfo object for type name or null.

◆ PrepareCustomScriptRun()

DLLEXPORT std::unique_ptr< CustomScriptRun > ScriptExecutor::PrepareCustomScriptRun ( asIScriptFunction *  func,
ScriptRunningSetup  extraoptions = ScriptRunningSetup() 
)

Starts a script run that supports custom argument passing.

Definition at line 350 of file ScriptExecutor.cpp.

352 {
353  if(!func)
354  return nullptr;
355 
356  auto run = std::make_unique<CustomScriptRun>(this);
357  run->Setup = extraoptions;
358  run->Func = func;
359 
360  // // TODO: this is a performance waste if there are no errors
361  // std::shared_ptr<ScriptModule> module =
362  // GetScriptModuleByFunction(func, run->Setup.PrintErrors);
363 
364  // Create a running context for the function //
365  run->Context = _GetContextForExecution();
366 
367  if(!run->Context) {
368  // Should this be fatal?
369  LOG_ERROR("ScriptExecutor: PrepareCustomScriptRun: failed to create a new context");
370  return nullptr;
371  }
372 
373  if(!_PrepareContextForPassingParameters(
374  func, run->Context, run->Setup, run->Module.get())) {
375 
376  _DoneWithContext(run->Context);
377  return nullptr;
378  }
379 
380  return run;
381 }
#define LOG_ERROR(x)
Definition: Define.h:83
DLLEXPORT void _DoneWithContext(asIScriptContext *context)
Called after a script has been executed and the context is no longer needed.
DLLEXPORT asIScriptContext * _GetContextForExecution()
Called when a context is required for script execution.

◆ PrintCallstack()

DLLEXPORT void ScriptExecutor::PrintCallstack ( asIScriptContext *  ctx,
LErrorReporter output 
)
static

Definition at line 640 of file ScriptExecutor.cpp.

641 {
642 
643  // Print callstack as additional information //
644  output.WriteLine("// ------------------ CallStack ------------------ //");
645 
646  // Loop the stack starting from the frame below the current function
647  // (actually might be nice to print the top frame too)
648  for(asUINT n = 0; n < ctx->GetCallstackSize(); n++) {
649 
650  // Get the function object //
651  const asIScriptFunction* function = ctx->GetFunction(n);
652 
653  // If the function doesn't exist this frame is used internally by the script engine //
654  if(function) {
655 
656  // Check function type //
657  if(function->GetFuncType() == asFUNC_SCRIPT) {
658 
659  // Print info about the script function //
660  output.WriteLine(std::string("\t> ") + function->GetScriptSectionName() + ":" +
661  std::to_string(ctx->GetLineNumber(n)) + " " +
662  function->GetDeclaration());
663 
664  } else {
665  // Info about the application functions //
666  // The context is being reused by the application for a nested call
667  output.WriteLine(
668  std::string("\t> {...Application...}: ") + function->GetDeclaration());
669  }
670  } else {
671  // The context is being reused by the script engine for a nested call
672  output.WriteLine("\t> {...Script internal...}");
673  }
674  }
675 }
virtual void WriteLine(const std::string &Text)=0

◆ PrintExceptionInfo()

DLLEXPORT void ScriptExecutor::PrintExceptionInfo ( asIScriptContext *  ctx,
LErrorReporter output,
asIScriptFunction *  func = nullptr,
ScriptModule scrptmodule = nullptr 
)
static

Prints exception info and stacktrace to a logger.

Definition at line 611 of file ScriptExecutor.cpp.

614 {
615 
616  std::string declaration = ctx->GetExceptionFunction()->GetDeclaration() ?
617  ctx->GetExceptionFunction()->GetDeclaration() :
618  "unknown function";
619 
620  std::string section = ctx->GetExceptionFunction()->GetScriptSectionName() ?
621  ctx->GetExceptionFunction()->GetScriptSectionName() :
622  "unknown";
623 
624  std::string exception =
625  ctx->GetExceptionString() ? ctx->GetExceptionString() : "unknown exception";
626 
627  std::string funcDeclaration =
628  func ? (func->GetDeclaration() ? func->GetDeclaration() : "unknown function") : "";
629 
630  output.Error(
631  std::string("[SCRIPT][EXCEPTION] ") + exception +
632  (func ? std::string(", while running function: ") + funcDeclaration : std::string()) +
633  "\n\t in function " + declaration + " defined in " + section + "(" +
634  std::to_string(ctx->GetExceptionLineNumber()) + ") " +
635  (scriptmodule ? scriptmodule->GetInfoString() : std::string()));
636 
637  PrintCallstack(ctx, output);
638 }
static DLLEXPORT void PrintCallstack(asIScriptContext *ctx, LErrorReporter &output)
virtual void Error(const std::string &Text)=0

◆ ResolveStringToASID()

DLLEXPORT int ScriptExecutor::ResolveStringToASID ( const char *  str) const

Converts a string to angelscript type id. Returns -1 on error.

Replaces GetAngelScriptTypeID

Definition at line 677 of file ScriptExecutor.cpp.

678 {
679 
680  return engine->GetTypeIdByDecl(str);
681 }

◆ RunReleaseRefOnObject()

DLLEXPORT void ScriptExecutor::RunReleaseRefOnObject ( void *  obj,
int  objid 
)

Finds release ref behaviour on object and calls it.

Exceptions
Exceptionif it didn't work
Note
Should only be called if the object of type has asOBJ_REF and no asOBJ_NOCOUNT
Todo:
This is actually a method in the angelscript engine, so use that, but this is a good example how to run any behaviour

Definition at line 315 of file ScriptExecutor.cpp.

316 {
317  asITypeInfo* info = engine->GetTypeInfoById(objid);
318 
319  asUINT count = info->GetBehaviourCount();
320 
321  for(asUINT i = 0; i < count; ++i) {
322 
323  asEBehaviours behaviour;
324  asIScriptFunction* func = info->GetBehaviourByIndex(i, &behaviour);
325 
326  if(!func) {
327 
328  LOG_ERROR("ScriptExecutor: RunReleaseRefOnObject: failed to get behaviour");
329  continue;
330  }
331 
332  if(behaviour == asBEHAVE_RELEASE) {
333 
334  LOG_INFO(
335  "ScriptExecutor: RunReleaseRefOnObject: Found asBEHAVE_RELEASE, calling it");
336 
337  ScriptRunningSetup ssetup;
338  const auto result = RunScriptMethod<void>(ssetup, func, obj);
339 
340  if(result.Result != SCRIPT_RUN_RESULT::Success)
341  throw Exception("Failed to run release behaviour");
342 
343  return;
344  }
345  }
346 
347  throw Exception("Didn't find release ref behaviour on object type");
348 }
#define LOG_INFO(x)
Definition: Define.h:81
#define LOG_ERROR(x)
Definition: Define.h:83
Base class for all exceptions thrown by Leviathan.
Definition: Exceptions.h:10

◆ RunScript() [1/2]

template<typename ReturnT , class... Args>
ScriptRunResult<ReturnT> Leviathan::ScriptExecutor::RunScript ( const std::shared_ptr< ScriptModule > &  module,
ScriptRunningSetup parameters,
Args &&...  args 
)
inline

Runs a function in a script.

Note
This is the recommended way to run scripts (other than GameModule that has its own method)
Todo:

Allow recursive calls and more context reuse. Also wrap context in an object that automatically returns it in case of expections (_HandleEndedScriptExecution can throw)

Also make all the script module using functions automatically get it if they need for error reporting

Definition at line 84 of file ScriptExecutor.h.

86  {
87  // This calls _CheckScriptFunctionPtr
88  asIScriptFunction* func = GetFunctionFromModule(module.get(), parameters);
89 
90  return RunScript<ReturnT>(func, module, parameters, std::forward<Args>(args)...);
91  }
DLLEXPORT asIScriptFunction * GetFunctionFromModule(ScriptModule *module, ScriptRunningSetup &parameters)
Finds a script function in module matching setup.

◆ RunScript() [2/2]

template<typename ReturnT , class... Args>
ScriptRunResult<ReturnT> Leviathan::ScriptExecutor::RunScript ( asIScriptFunction *  func,
std::shared_ptr< ScriptModule module,
ScriptRunningSetup parameters,
Args &&...  args 
)
inline

Runs a function in a script (that is known already)

Todo:
Wrap context in an object that automatically returns it in case of expections (_HandleEndedScriptExecution can throw)

Definition at line 97 of file ScriptExecutor.h.

99  {
100  if(!func)
101  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
102 
103  if(!module) {
104  // TODO: this is a performance waste if there are no errors
105  // Find the right module //
106  module = GetScriptModuleByFunction(func, parameters.PrintErrors);
107  }
108 
109 
110  // Create a running context for the function //
111  asIScriptContext* scriptContext = _GetContextForExecution();
112 
113  if(!scriptContext) {
114  // Should this be fatal?
115  LOG_ERROR("ScriptExecutor: RunScript: failed to create a new context");
116  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
117  }
118 
119  if(!_PrepareContextForPassingParameters(
120  func, scriptContext, parameters, module.get())) {
121 
122  _DoneWithContext(scriptContext);
123  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
124  }
125 
126  // Pass the parameters //
127  if(!_PassParametersToScript(
128  scriptContext, parameters, module.get(), func, std::forward<Args>(args)...)) {
129 
130  // Failed passing the parameters //
131  _DoneWithContext(scriptContext);
132  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
133  }
134 
135  // Run the script //
136  // TODO: timeout and debugging registering with linecallbacks here //
137  int retcode = scriptContext->Execute();
138 
139  // Get the return value //
140  auto returnvalue = _HandleEndedScriptExecution<ReturnT>(
141  retcode, scriptContext, parameters, func, module.get());
142 
143  // Release the context //
144  _DoneWithContext(scriptContext);
145 
146  // Return the returned value //
147  return returnvalue;
148  }
#define LOG_ERROR(x)
Definition: Define.h:83
DLLEXPORT std::shared_ptr< ScriptModule > GetScriptModuleByFunction(asIScriptFunction *func, bool reporterror)
Returns module in which script function was defined in.
DLLEXPORT void _DoneWithContext(asIScriptContext *context)
Called after a script has been executed and the context is no longer needed.
DLLEXPORT asIScriptContext * _GetContextForExecution()
Called when a context is required for script execution.

◆ RunScriptMethod()

template<typename ReturnT , class... Args>
ScriptRunResult<ReturnT> Leviathan::ScriptExecutor::RunScriptMethod ( ScriptRunningSetup parameters,
asIScriptFunction *  func,
void *  obj,
Args &&...  args 
)
inline

Runs a method in a script.

Note
This doesn't verify that the object type is correct for the function. The caller is responsible for making sure that func is part of the class of obj
The parameters object is largely ignored (function name)
Todo:
Merge common parts with RunScript

Definition at line 156 of file ScriptExecutor.h.

158  {
159  if(!func || !obj)
160  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
161 
162  // TODO: this is a performance waste if there are no errors
163  std::shared_ptr<ScriptModule> module =
164  GetScriptModuleByFunction(func, parameters.PrintErrors);
165 
166  // Create a running context for the function //
167  asIScriptContext* scriptContext = _GetContextForExecution();
168 
169  if(!scriptContext) {
170  // Should this be fatal?
171  LOG_ERROR("ScriptExecutor: RunScriptMethod: failed to create a new context");
172  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
173  }
174 
175  if(!_PrepareContextForPassingParameters(
176  func, scriptContext, parameters, module.get())) {
177 
178  _DoneWithContext(scriptContext);
179  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
180  }
181 
182  // Pass the parameters //
183  if(!_PassParametersToScript(
184  scriptContext, parameters, module.get(), func, std::forward<Args>(args)...)) {
185 
186  // Failed passing the parameters //
187  _DoneWithContext(scriptContext);
188  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
189  }
190 
191  // Pass the object instance //
192  if(scriptContext->SetObject(obj) < 0) {
193 
194  _DoneWithContext(scriptContext);
195  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
196  }
197 
198  // Run the script //
199  // TODO: timeout and debugging registering with linecallbacks here //
200  int retcode = scriptContext->Execute();
201 
202  // Get the return value //
203  auto returnvalue = _HandleEndedScriptExecution<ReturnT>(
204  retcode, scriptContext, parameters, func, module.get());
205 
206  // Release the context //
207  _DoneWithContext(scriptContext);
208 
209  // Return the returned value //
210  return returnvalue;
211  }
#define LOG_ERROR(x)
Definition: Define.h:83
DLLEXPORT std::shared_ptr< ScriptModule > GetScriptModuleByFunction(asIScriptFunction *func, bool reporterror)
Returns module in which script function was defined in.
DLLEXPORT void _DoneWithContext(asIScriptContext *context)
Called after a script has been executed and the context is no longer needed.
DLLEXPORT asIScriptContext * _GetContextForExecution()
Called when a context is required for script execution.

Friends And Related Function Documentation

◆ RequestContextCallback

asIScriptContext* RequestContextCallback ( asIScriptEngine *  engine,
void *  userdata 
)
friend

Definition at line 99 of file ScriptExecutor.cpp.

100 {
101  return static_cast<ScriptExecutor*>(userdata)->_GetContextForExecution();
102 }
Handles ScriptModule creation and AngelScript code execution.
DLLEXPORT asIScriptContext * _GetContextForExecution()
Called when a context is required for script execution.

◆ ReturnContextCallback

void ReturnContextCallback ( asIScriptEngine *  engine,
asIScriptContext *  context,
void *  userdata 
)
friend

Definition at line 104 of file ScriptExecutor.cpp.

105 {
106  static_cast<ScriptExecutor*>(userdata)->_DoneWithContext(context);
107 }
Handles ScriptModule creation and AngelScript code execution.
DLLEXPORT void _DoneWithContext(asIScriptContext *context)
Called after a script has been executed and the context is no longer needed.

The documentation for this class was generated from the following files: