Leviathan  0.8.0.0
Leviathan game engine
ScriptExecutor.h
Go to the documentation of this file.
1 // Leviathan Game Engine
2 // Copyright (c) 2012-2018 Henri Hyyryläinen
3 #pragma once
4 #include "Define.h"
5 // ------------------------------------ //
7 #include "Common/ThreadSafe.h"
8 #include "Handlers/IDFactory.h"
11 #include "Script/ScriptScript.h"
13 
14 #include <memory>
15 #include <string>
16 #include <vector>
17 
18 // angelscript //
19 //#define ANGELSCRIPT_DLL_LIBRARY_IMPORT
20 #include "angelscript.h"
21 
22 
23 #if ANGELSCRIPT_VERSION >= 23300
24 #define ANGELSCRIPT_HAS_TRANSLATE_CALLBACK
25 #endif
26 
27 namespace Leviathan {
28 
29 class ScriptExecutor;
30 
35  friend ScriptExecutor;
36 
37 public:
39  inline CustomScriptRun(ScriptExecutor* exec) : Exec(exec){};
41 
43  asIScriptFunction* Func;
44  asIScriptContext* Context;
45  std::shared_ptr<ScriptModule> Module;
46 
47  // For keeping track of parameter number (not used by ScriptExecutor but is used by helpers
48  // in CustomScriptRunHelpers.h)
49  asUINT PassedIndex = 0;
50 
51 private:
52  ScriptExecutor* Exec;
53 };
54 
57  friend CustomScriptRun;
58  friend asIScriptContext* RequestContextCallback(asIScriptEngine* engine, void* userdata);
59  friend void ReturnContextCallback(
60  asIScriptEngine* engine, asIScriptContext* context, void* userdata);
61 
62 public:
65 
66  // ------------------------------------ //
67  // module managing
68  DLLEXPORT std::weak_ptr<ScriptModule> CreateNewModule(const std::string& name,
69  const std::string& source, const int& modulesid = IDFactory::GetID());
70 
71  DLLEXPORT void DeleteModule(ScriptModule* ptrtomatch);
73  DLLEXPORT std::weak_ptr<ScriptModule> GetModule(const int& ID);
74  DLLEXPORT std::weak_ptr<ScriptModule> GetModuleByAngelScriptName(const char* nameofmodule);
75 
83  template<typename ReturnT, class... Args>
84  ScriptRunResult<ReturnT> RunScript(const std::shared_ptr<ScriptModule>& module,
85  ScriptRunningSetup& parameters, Args&&... args)
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  }
92 
96  template<typename ReturnT, class... Args>
97  ScriptRunResult<ReturnT> RunScript(asIScriptFunction* func,
98  std::shared_ptr<ScriptModule> module, ScriptRunningSetup& parameters, Args&&... args)
99  {
100  if(!func)
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");
117  }
118 
119  if(!_PrepareContextForPassingParameters(
120  func, scriptContext, parameters, module.get())) {
121 
122  _DoneWithContext(scriptContext);
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);
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  }
149 
155  template<typename ReturnT, class... Args>
157  ScriptRunningSetup& parameters, asIScriptFunction* func, void* obj, Args&&... args)
158  {
159  if(!func || !obj)
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");
173  }
174 
175  if(!_PrepareContextForPassingParameters(
176  func, scriptContext, parameters, module.get())) {
177 
178  _DoneWithContext(scriptContext);
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);
189  }
190 
191  // Pass the object instance //
192  if(scriptContext->SetObject(obj) < 0) {
193 
194  _DoneWithContext(scriptContext);
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  }
212 
214  DLLEXPORT std::unique_ptr<CustomScriptRun> PrepareCustomScriptRun(
215  asIScriptFunction* func, ScriptRunningSetup extraoptions = ScriptRunningSetup());
216 
220  template<typename ReturnT>
221  ScriptRunResult<ReturnT> ExecuteCustomRun(const std::unique_ptr<CustomScriptRun>& run)
222  {
223  if(!run)
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  }
241 
242 
248  DLLEXPORT void RunReleaseRefOnObject(void* obj, int objid);
249 
253  DLLEXPORT std::shared_ptr<ScriptModule> GetScriptModuleByFunction(
254  asIScriptFunction* func, bool reporterror);
255 
260  DLLEXPORT asIScriptFunction* GetFunctionFromModule(
261  ScriptModule* module, ScriptRunningSetup& parameters);
262 
267  DLLEXPORT int ResolveStringToASID(const char* str, bool constversion = false) const;
268 
270  DLLEXPORT asITypeInfo* GetTypeInfo(int type) const;
271 
273  DLLEXPORT asITypeInfo* GetTypeInfoByDecl(const char* str) const;
274 
275  DLLEXPORT inline asIScriptEngine* GetASEngine()
276  {
277  return engine;
278  }
279 
281  DLLEXPORT void CollectGarbage();
282 
284  DLLEXPORT static void PrintExceptionInfo(asIScriptContext* ctx, LErrorReporter& output,
285  asIScriptFunction* func = nullptr, ScriptModule* scrptmodule = nullptr);
286 
287  DLLEXPORT static void PrintCallstack(asIScriptContext* ctx, LErrorReporter& output);
288 
290  DLLEXPORT static ScriptExecutor* Get();
291 
292 private:
295  template<class CurrentT, class... Args>
296  bool _DoPassEachParameter(asUINT parameterc, asUINT& i, asIScriptContext* scriptcontext,
297  ScriptRunningSetup& setup, ScriptModule* module, asIScriptFunction* func,
298  const CurrentT& current, Args&&... args)
299  {
300  // Finished passing parameters //
301  if(i >= parameterc)
302  return true;
303 
304  // Try to pass based on type //
305  int r = 0;
306 
307  // Check that type matches //
308  int wantedTypeID;
309  asDWORD flags;
310  if(func->GetParam(i, &wantedTypeID, &flags) < 0) {
311 
312  LOG_ERROR("ScriptExecutor: failed to get param type from as: " +
313  std::to_string(i) + ", for func: " + func->GetName());
314  return false;
315  }
316 
317 
318  // if constexpr(CanTypeRepresentAngelScriptTypes<CurrentT>()) {
319 
320  // } else {
321  // }
322 
323 
324  const auto parameterType = AngelScriptTypeIDResolver<CurrentT>::Get(this);
325 
326  bool matched = false;
327 
328  matched = wantedTypeID == parameterType;
329 
330  // Allow taking a non-const object into the script as a const object
331  if(!matched) {
332  if(wantedTypeID & asTYPEID_HANDLETOCONST) {
333 
334  if((parameterType & asTYPEID_HANDLETOCONST) == wantedTypeID) {
335  matched = true;
336  }
337  }
338  }
339 
340  if(!matched) {
341 
342  // Compatibility checks //
343  // This is not the most optimal as this results in a duplicate call to
344  // func->GetParam
345  // TODO: is there a better way than to have this mess here?
346 
347  if(wantedTypeID == AngelScriptTypeIDResolver<int32_t>::Get(this)) {
348 
349  if constexpr(std::is_convertible_v<CurrentT, int32_t>) {
350  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
351  func, static_cast<int32_t>(current), std::forward<Args>(args)...);
352  }
353  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint32_t>::Get(this)) {
354 
355  if constexpr(std::is_convertible_v<CurrentT, uint32_t>) {
356  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
357  func, static_cast<uint32_t>(current), std::forward<Args>(args)...);
358  }
359  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint64_t>::Get(this)) {
360 
361  if constexpr(std::is_convertible_v<CurrentT, uint64_t>) {
362  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
363  func, static_cast<uint64_t>(current), std::forward<Args>(args)...);
364  }
365  } else if(wantedTypeID == AngelScriptTypeIDResolver<int64_t>::Get(this)) {
366 
367  if constexpr(std::is_convertible_v<CurrentT, int64_t>) {
368  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
369  func, static_cast<int64_t>(current), std::forward<Args>(args)...);
370  }
371  } else if(wantedTypeID == AngelScriptTypeIDResolver<int16_t>::Get(this)) {
372 
373  if constexpr(std::is_convertible_v<CurrentT, int16_t>) {
374  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
375  func, static_cast<int16_t>(current), std::forward<Args>(args)...);
376  }
377  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint16_t>::Get(this)) {
378 
379  if constexpr(std::is_convertible_v<CurrentT, uint16_t>) {
380  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
381  func, static_cast<uint16_t>(current), std::forward<Args>(args)...);
382  }
383  } else if(wantedTypeID == AngelScriptTypeIDResolver<int8_t>::Get(this)) {
384 
385  if constexpr(std::is_convertible_v<CurrentT, int8_t>) {
386  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
387  func, static_cast<int8_t>(current), std::forward<Args>(args)...);
388  }
389 
390  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint8_t>::Get(this)) {
391 
392  if constexpr(std::is_convertible_v<CurrentT, uint8_t>) {
393  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
394  func, static_cast<uint8_t>(current), std::forward<Args>(args)...);
395  }
396  } else if(wantedTypeID == AngelScriptTypeIDResolver<float>::Get(this)) {
397 
398  if constexpr(std::is_convertible_v<CurrentT, float>) {
399  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
400  func, static_cast<float>(current), std::forward<Args>(args)...);
401  }
402 
403  } else if(wantedTypeID == AngelScriptTypeIDResolver<double>::Get(this)) {
404 
405  if constexpr(std::is_convertible_v<CurrentT, double>) {
406  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
407  func, static_cast<double>(current), std::forward<Args>(args)...);
408  }
409  }
410 
411  // No conversion possible //
412  return _DoPassParameterTypeError(setup, module, i, wantedTypeID, parameterType);
413  }
414 
415  if constexpr(std::is_same_v<CurrentT, int32_t> || std::is_same_v<CurrentT, uint32_t>) {
416 
417  r = scriptcontext->SetArgDWord(i, current);
418  } else if constexpr(std::is_same_v<CurrentT, int64_t> ||
419  std::is_same_v<CurrentT, uint64_t>) {
420 
421  r = scriptcontext->SetArgQWord(i, current);
422  } else if constexpr(std::is_same_v<CurrentT, int16_t> ||
423  std::is_same_v<CurrentT, uint16_t>) {
424 
425  r = scriptcontext->SetArgWord(i, current);
426  } else if constexpr(std::is_same_v<CurrentT, float>) {
427 
428  r = scriptcontext->SetArgFloat(i, current);
429  } else if constexpr(std::is_same_v<CurrentT, double>) {
430 
431  r = scriptcontext->SetArgDouble(i, current);
432  } else if constexpr(std::is_same_v<CurrentT, char> ||
433  std::is_same_v<CurrentT, int8_t> ||
434  std::is_same_v<CurrentT, bool>) {
435 
436  r = scriptcontext->SetArgByte(i, current);
437 
439  } else if constexpr(std::is_same_v<CurrentT, std::wstring>) {
440 
441  static_assert(!std::is_same_v<CurrentT, std::wstring>,
442  "wstring would have to be saved as a string that can then be passed");
443 
444  } else {
445  // Non-primitive type //
446 
447  // TODO: adding a test to verify this would be nice
448  // Checks for const being used correctly are already done,
449  // so this always does const away cast
450 
451  // Checks for pointers and references to things with type id verification //
452  if constexpr(std::is_pointer_v<CurrentT>) {
453 
455 
456  r = scriptcontext->SetArgAddress(
457  i, const_cast<std::add_pointer_t<
458  std::remove_const_t<std::remove_pointer_t<CurrentT>>>>(current));
459  } else if constexpr(std::is_lvalue_reference_v<CurrentT>) {
460 
462 
463  r = scriptcontext->SetArgAddress(
464  i, &const_cast<std::remove_const_t<std::remove_reference_t<CurrentT>>>(
465  current));
466  } else if constexpr(std::is_class_v<CurrentT>) {
467 
468  // Has to be a class that isn't a handle type
469  // So make sure it isn't
470  static_assert(!std::is_base_of_v<CurrentT, ReferenceCounted>,
471  "Trying to pass an object of reference type by value to script, call new "
472  "on the argument");
473 
474  // If this is by reference, then it must be const
475  if((flags & asTM_OUTREF)) {
476 
477  LOG_ERROR(
478  "ScriptExecutor: script wants to take parameter: " +
479  std::to_string(i) +
480  " as an outref which isn't supported, for func: " + func->GetName());
481  return false;
482 
483  } else if((flags & asTM_INREF) && !(flags & asTM_CONST)) {
484 
485  LOG_ERROR(
486  "ScriptExecutor: script wants to take parameter: " +
487  std::to_string(i) +
488  " as non-const inref which isn't supported (add const), for func: " +
489  func->GetName());
490  return false;
491 
492  } else {
493 
494  r = scriptcontext->SetArgObject(i, const_cast<CurrentT*>(&current));
495  }
496 
497  } else {
498 
499  static_assert(std::is_same_v<CurrentT, void> == std::is_same_v<CurrentT, int>,
500  "Tried to pass some very weird type to a script function");
501  }
502  }
503 
504  // Move to next parameter for the next recursive call //
505  ++i;
506 
507  // Error check //
508  if(r < 0) {
509  LOG_ERROR("ScriptExecutor: failed to pass parameter number: " +
510  std::to_string(i - 1) + ", for func: " + func->GetName());
511  return false;
512  }
513 
514  // Call other parameters //
515  return _DoPassEachParameter(
516  parameterc, i, scriptcontext, setup, module, func, std::forward<Args>(args)...);
517  }
518 
519 
523  template<typename ReturnT>
524  ScriptRunResult<ReturnT> _HandleEndedScriptExecution(int retcode,
525  asIScriptContext* scriptcontext, ScriptRunningSetup& setup, asIScriptFunction* func,
526  ScriptModule* module)
527  {
528  // Check the return type //
529  if(retcode != asEXECUTION_FINISHED) {
530  // something went wrong //
531 
532  // The execution didn't finish as we had planned. Determine why.
533  switch(retcode) {
534  // script caused an exception //
535  case asEXECUTION_EXCEPTION:
536  PrintExceptionInfo(scriptcontext, *Logger::Get(),
537  scriptcontext->GetExceptionFunction(), module);
538  [[fallthrough]];
539  default:
540  // code took too long //
541  case asEXECUTION_ABORTED:
542  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
543  case asEXECUTION_SUSPENDED:
544  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Suspended);
545  }
546  }
547 
548  // Successfully executed, try to fetch return value //
549 
550  const auto returnType = func->GetReturnTypeId();
551 
552  // Script didn't return anything
553  if(returnType == ANGELSCRIPT_VOID_TYPEID) {
554 
555  // Set the return value to default if it isn't void //
556  if constexpr(std::is_void_v<ReturnT>) {
557  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success);
558  } else {
559 
560  if(setup.PrintErrors) {
561 
562  if constexpr(std::is_same_v<asIScriptObject*, ReturnT>) {
563 
564  LOG_ERROR(
565  "ScriptExecutor: script return value is void, but application "
566  "expected a value of type: any script object implementing the "
567  "wanted interface");
568 
569  } else if constexpr(std::is_same_v<asIScriptFunction*, ReturnT>) {
570 
571  LOG_ERROR(
572  "ScriptExecutor: script return value is void, but application "
573  "expected a value of type: any script function matching the "
574  "wanted funcdef");
575 
576  } else if constexpr(CanTypeRepresentAngelScriptTypes<ReturnT>()) {
577  LOG_ERROR(
578  "ScriptExecutor: script return value is void, but application "
579  "expected a value of type: other class that can have many "
580  "angelscript types represented by it");
581  } else {
582  LOG_ERROR(
583  "ScriptExecutor: script return value is void, but application "
584  "expected a value of type: " +
585  std::string(typeid(ReturnT).name()) + " id: " +
586  std::to_string(AngelScriptTypeIDResolver<ReturnT>::Get(this)));
587  }
588 
589  LOG_INFO(
590  "ScriptExecutor: while running function: " +
591  (func ? std::string(func->GetDeclaration()) : setup.Entryfunction));
592  }
593 
594  // Rely on 0 being a valid value for pointer etc.
595  if constexpr(std::is_pointer_v<ReturnT> || !std::is_class_v<ReturnT>) {
596  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success, 0);
597  } else {
598 
599  // Default constructor needs to be available
600  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success, ReturnT());
601  }
602  }
603  }
604 
605  // script return type isn't void //
606  if constexpr(std::is_same_v<ReturnT, void>) {
607 
608  const auto parameterType = AngelScriptTypeIDResolver<ReturnT>::Get(this);
609 
610  // Success, no return value wanted //
611  if(setup.PrintErrors && (returnType != parameterType)) {
612 
613  LOG_WARNING("ScriptExecutor: application ignoring script return value");
614  }
615 
616  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success);
617 
618  } else {
619 
620  if constexpr(CanTypeRepresentAngelScriptTypes<ReturnT>()) {
621 
622  // We are getting a generic asIScriptObject or asIScriptFunction
623  asITypeInfo* info = GetTypeInfo(returnType);
624  const auto flags = info->GetFlags();
625 
626  // Verify type //
627  if constexpr(std::is_same_v<ReturnT, asIScriptObject*>) {
628 
629  if(!(flags & asOBJ_SCRIPT_OBJECT)) {
630 
631  LOG_ERROR("ScriptExecutor: application expected a script object but "
632  "script returned: " +
633  std::string(info->GetName()));
634  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
635  }
636 
637  } else if constexpr(std::is_same_v<ReturnT, asIScriptFunction*>) {
638 
639  if(!(flags & asOBJ_FUNCDEF)) {
640 
641  LOG_ERROR("ScriptExecutor: application expected a script function but "
642  "script returned: " +
643  std::string(info->GetName()));
644  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
645  }
646 
647  } else {
648 
649  LOG_FATAL("Unkown angelscript multi type class");
650  }
651 
652  ReturnT obj = static_cast<ReturnT>(scriptcontext->GetReturnObject());
653 
655  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success, std::move(obj));
656 
657  } else {
658 
659  const auto parameterType = AngelScriptTypeIDResolver<ReturnT>::Get(this);
660 
661  // TODO: conversions between compatible types
662  if(returnType != parameterType) {
663 
664  return _ReturnedTypeDidntMatch<ReturnT>(
665  scriptcontext, setup, func, module, parameterType, returnType);
666  }
667 
668  if constexpr(std::is_same_v<ReturnT, int32_t> ||
669  std::is_same_v<ReturnT, uint32_t>) {
670 
671  return ScriptRunResult<ReturnT>(
672  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnDWord());
673  } else if constexpr(std::is_same_v<ReturnT, int64_t> ||
674  std::is_same_v<ReturnT, uint64_t>) {
675 
676  return ScriptRunResult<ReturnT>(
677  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnQWord());
678  } else if constexpr(std::is_same_v<ReturnT, float>) {
679 
680  return ScriptRunResult<ReturnT>(
681  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnFloat());
682  } else if constexpr(std::is_same_v<ReturnT, double>) {
683 
684  return ScriptRunResult<ReturnT>(
685  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnDouble());
686  } else if constexpr(std::is_same_v<ReturnT, char> ||
687  std::is_same_v<ReturnT, int8_t> ||
688  std::is_same_v<ReturnT, bool>) {
689 
690  return ScriptRunResult<ReturnT>(
691  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnByte());
692  } else {
693 
694  // This is a class type and we need to do a copy if it was
695  // by value or this isn't a handle type
696 
697  // According to AS documentation the return object is
698  // deleted when the context is recycled, so we need to
699  // increase ref or make a copy
700 
701  if constexpr(std::is_pointer_v<ReturnT>) {
702 
703  // We have already done type checks, so this should be fine to cast //
704  ReturnT obj = static_cast<ReturnT>(scriptcontext->GetReturnObject());
705 
707  return ScriptRunResult<ReturnT>(
708  SCRIPT_RUN_RESULT::Success, std::move(obj));
709 
710  } else if constexpr(std::is_lvalue_reference_v<ReturnT>) {
711 
712  static_assert(!std::is_class_v<ReturnT>,
713  "Returning by reference from scripts doesn't work");
714 
715  } else if constexpr(std::is_class_v<ReturnT>) {
716 
717  // We have already done type checks, so this should be fine to cast //
718  ReturnT* obj = static_cast<ReturnT*>(scriptcontext->GetReturnObject());
719  return ScriptRunResult<ReturnT>(
720  SCRIPT_RUN_RESULT::Success, std::move(*obj));
721  } else {
722 
723  static_assert(
724  std::is_same_v<ReturnT, void> == std::is_same_v<ReturnT, int>,
725  "Tried to return some very weird type from a script function");
726  }
727  }
728  }
729  }
730  }
731 
739  template<class... Args>
740  bool _PassParametersToScript(asIScriptContext* scriptcontext, ScriptRunningSetup& setup,
741  ScriptModule* module, asIScriptFunction* func, Args&&... args)
742  {
743  // Get the number of parameters expected //
744  auto parameterc = func->GetParamCount();
745  asUINT i = 0;
746 
747  // Start passing the parameters provided by the application //
748  if(!_DoPassEachParameter(
749  parameterc, i, scriptcontext, setup, module, func, std::forward<Args>(args)...))
750  return false;
751 
752  // Check that we passed enough parameters for the script (we
753  // don't care if the script took less parameters than we gave
754  // it)
755  if(i < parameterc) {
756  // We didn't have enough parameters
757  if(setup.PrintErrors) {
758  LOG_ERROR("ScriptExecutor: not enough parameters to pass to script function");
759  }
760 
761  return false;
762  }
763 
764  return true;
765  }
766 
767  // End condition for the variadic template
768  bool _DoPassEachParameter(asUINT parameterc, asUINT& i, asIScriptContext* scriptcontext,
769  ScriptRunningSetup& setup, ScriptModule* module, asIScriptFunction* func)
770  {
771  return true;
772  }
773 
775  template<typename ReturnT>
776  ScriptRunResult<ReturnT> _ReturnedTypeDidntMatch(asIScriptContext* scriptcontext,
777  ScriptRunningSetup& setup, asIScriptFunction* func, ScriptModule* module,
778  int parameterType, int returnType)
779  {
780  _DoReceiveParameterTypeError(setup, module, parameterType, returnType);
781  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
782  }
783 
789  DLLEXPORT bool _DoPassParameterTypeError(ScriptRunningSetup& setup, ScriptModule* module,
790  int i, int scriptwanted, int provided);
791 
794  DLLEXPORT void _DoReceiveParameterTypeError(
795  ScriptRunningSetup& setup, ScriptModule* module, int applicationwanted, int scripthad);
796 
797 
798 
802  void _CallBehaviourReleaseIfNeeded(void* obj, int returnTypeID)
803  {
804  // TODO: this should instead call engine->ReleaseScriptObject
805  asITypeInfo* info = GetASEngine()->GetTypeInfoById(returnTypeID);
806 
807  const auto flags = info->GetFlags();
808  if((flags & asOBJ_REF) && !(flags & asOBJ_NOCOUNT)) {
809 
810  LOG_INFO("ScriptExecutor: attempting to release ref through angelscript");
811 
812  RunReleaseRefOnObject(obj, returnTypeID);
813 
814  LOG_INFO("Success calling behaviour release");
815  }
816  }
817 
818 
819  // ------------------------------------ //
820 
822  DLLEXPORT bool _CheckScriptFunctionPtr(
823  asIScriptFunction* func, ScriptRunningSetup& parameters, ScriptModule* scrptmodule);
824 
826  DLLEXPORT bool _PrepareContextForPassingParameters(asIScriptFunction* func,
827  asIScriptContext* ScriptContext, ScriptRunningSetup& parameters,
828  ScriptModule* scriptmodule);
829 
830 protected:
833  DLLEXPORT asIScriptContext* _GetContextForExecution();
834 
837  DLLEXPORT void _DoneWithContext(asIScriptContext* context);
838 
839 private:
840  // AngelScript engine script executing part //
841  asIScriptEngine* engine;
842 
843  // list of modules that have been created, some might only have this as reference, and
844  // could potentially be released
845  std::vector<std::shared_ptr<ScriptModule>> AllocatedScriptModules;
846 
847  Mutex ModulesLock;
848 
850  std::vector<asIScriptContext*> ContextPool;
851 
853  Mutex ContextPoolLock;
854 
855  static ScriptExecutor* instance;
856 };
857 
858 } // namespace Leviathan
ScriptRunResult< ReturnT > RunScript(const std::shared_ptr< ScriptModule > &module, ScriptRunningSetup &parameters, Args &&... args)
Runs a function in a script.
ScriptRunningSetup Setup
DLLEXPORT asIScriptFunction * GetFunctionFromModule(ScriptModule *module, ScriptRunningSetup &parameters)
Finds a script function in module matching setup.
DLLEXPORT asIScriptEngine * GetASEngine()
#define LOG_INFO(x)
Definition: Define.h:88
ScriptRunResult< ReturnT > ExecuteCustomRun(const std::unique_ptr< CustomScriptRun > &run)
Ends a custom script run by actually executing the script and returning a value.
friend void ReturnContextCallback(asIScriptEngine *engine, asIScriptContext *context, void *userdata)
#define LOG_ERROR(x)
Definition: Define.h:90
DLLEXPORT std::shared_ptr< ScriptModule > GetScriptModuleByFunction(asIScriptFunction *func, bool reporterror)
Returns module in which script function was defined in.
static int Get(ScriptExecutor *resolver)
DLLEXPORT void RunReleaseRefOnObject(void *obj, int objid)
Finds release ref behaviour on object and calls it.
#define LOG_FATAL(x)
Definition: Define.h:92
constexpr auto ANGELSCRIPT_VOID_TYPEID
This has to be constant (and luckily so far it has been)
DLLEXPORT int ResolveStringToASID(const char *str, bool constversion=false) const
Converts a string to angelscript type id. Returns -1 on error.
DLLEXPORT std::weak_ptr< ScriptModule > GetModuleByAngelScriptName(const char *nameofmodule)
DLLEXPORT asITypeInfo * GetTypeInfo(int type) const
Returns an asITypeInfo object for type id or null.
CustomScriptRun(ScriptExecutor *exec)
Handles ScriptModule creation and AngelScript code execution.
DLLEXPORT void CollectGarbage()
Does a full garbage collection cycle.
static int GetID()
Definition: IDFactory.h:17
static DLLEXPORT void PrintCallstack(asIScriptContext *ctx, LErrorReporter &output)
static DLLEXPORT void PrintExceptionInfo(asIScriptContext *ctx, LErrorReporter &output, asIScriptFunction *func=nullptr, ScriptModule *scrptmodule=nullptr)
Prints exception info and stacktrace to a logger.
#define LOG_WARNING(x)
Definition: Define.h:89
DLLEXPORT std::weak_ptr< ScriptModule > CreateNewModule(const std::string &name, const std::string &source, const int &modulesid=IDFactory::GetID())
Holds a result of the new script run method.
static DLLEXPORT ScriptExecutor * Get()
DLLEXPORT bool DeleteModuleIfNoExternalReferences(int ID)
Contains data for script runs where arguments are passed manually.
std::shared_ptr< ScriptModule > Module
std::mutex Mutex
Definition: ThreadSafe.h:16
DLLEXPORT asITypeInfo * GetTypeInfoByDecl(const char *str) const
Returns an asITypeInfo object for type name or null.
static DLLEXPORT Logger * Get()
Definition: Logger.cpp:106
ScriptRunResult< ReturnT > RunScriptMethod(ScriptRunningSetup &parameters, asIScriptFunction *func, void *obj, Args &&... args)
Runs a method in a script.
asIScriptFunction * Func
#define DLLEXPORT
Definition: Include.h:84
DLLEXPORT void _DoneWithContext(asIScriptContext *context)
Called after a script has been executed and the context is no longer needed.
DLLEXPORT std::unique_ptr< CustomScriptRun > PrepareCustomScriptRun(asIScriptFunction *func, ScriptRunningSetup extraoptions=ScriptRunningSetup())
Starts a script run that supports custom argument passing.
DLLEXPORT std::weak_ptr< ScriptModule > GetModule(const int &ID)
DLLEXPORT void DeleteModule(ScriptModule *ptrtomatch)
The access mask controls which registered functions and classes a script sees.
Definition: GameModule.h:12
ScriptRunResult< ReturnT > RunScript(asIScriptFunction *func, std::shared_ptr< ScriptModule > module, ScriptRunningSetup &parameters, Args &&... args)
Runs a function in a script (that is known already)
friend asIScriptContext * RequestContextCallback(asIScriptEngine *engine, void *userdata)
static void IncrementRefCountIfRefCountedType(T *current)
Increments refcount of obj if it is derived from ReferenceCounted or an angelscript type.
DLLEXPORT asIScriptContext * _GetContextForExecution()
Called when a context is required for script execution.
asIScriptContext * Context