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-2019 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 std::string GetTypeName(int type) const;
274 
276  DLLEXPORT asITypeInfo* GetTypeInfoByDecl(const char* str) const;
277 
278  DLLEXPORT inline asIScriptEngine* GetASEngine()
279  {
280  return engine;
281  }
282 
284  DLLEXPORT void CollectGarbage();
285 
287  DLLEXPORT static void PrintExceptionInfo(asIScriptContext* ctx, LErrorReporter& output,
288  asIScriptFunction* func = nullptr, ScriptModule* scrptmodule = nullptr);
289 
290  DLLEXPORT static void PrintCallstack(asIScriptContext* ctx, LErrorReporter& output);
291 
293  DLLEXPORT static ScriptExecutor* Get();
294 
295 private:
298  template<class CurrentT, class... Args>
299  bool _DoPassEachParameter(asUINT parameterc, asUINT& i, asIScriptContext* scriptcontext,
300  ScriptRunningSetup& setup, ScriptModule* module, asIScriptFunction* func,
301  const CurrentT& current, Args&&... args)
302  {
303  // Finished passing parameters //
304  if(i >= parameterc)
305  return true;
306 
307  // Try to pass based on type //
308  int r = 0;
309 
310  // Check that type matches //
311  int wantedTypeID;
312  asDWORD flags;
313  if(func->GetParam(i, &wantedTypeID, &flags) < 0) {
314 
315  LOG_ERROR("ScriptExecutor: failed to get param type from as: " +
316  std::to_string(i) + ", for func: " + func->GetName());
317  return false;
318  }
319 
320 
321  // if constexpr(CanTypeRepresentAngelScriptTypes<CurrentT>()) {
322 
323  // } else {
324  // }
325 
326  int parameterType;
327 
328  // Script object has a method to retrieve the type id it contains
329  if constexpr(std::is_same_v<CurrentT, asIScriptObject*>) {
330  // Handle to script object is implicit
331  parameterType = current->GetTypeId() | asTYPEID_OBJHANDLE;
332  } else {
333  parameterType = AngelScriptTypeIDResolver<CurrentT>::Get(this);
334  }
335 
336  bool matched = false;
337 
338  matched = wantedTypeID == parameterType;
339 
340  if(!matched) {
341  // Allow taking a non-const object into the script as a const object
342  if(wantedTypeID & asTYPEID_HANDLETOCONST) {
343 
344  if((parameterType | asTYPEID_HANDLETOCONST) == wantedTypeID) {
345  matched = true;
346  }
347  }
348  }
349 
350  if(!matched) {
351 
352  // Compatibility checks //
353  // This is not the most optimal as this results in a duplicate call to
354  // func->GetParam
355  // TODO: is there a better way than to have this mess here?
356 
357  if(wantedTypeID == AngelScriptTypeIDResolver<int32_t>::Get(this)) {
358 
359  if constexpr(std::is_convertible_v<CurrentT, int32_t>) {
360  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
361  func, static_cast<int32_t>(current), std::forward<Args>(args)...);
362  }
363  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint32_t>::Get(this)) {
364 
365  if constexpr(std::is_convertible_v<CurrentT, uint32_t>) {
366  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
367  func, static_cast<uint32_t>(current), std::forward<Args>(args)...);
368  }
369  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint64_t>::Get(this)) {
370 
371  if constexpr(std::is_convertible_v<CurrentT, uint64_t>) {
372  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
373  func, static_cast<uint64_t>(current), std::forward<Args>(args)...);
374  }
375  } else if(wantedTypeID == AngelScriptTypeIDResolver<int64_t>::Get(this)) {
376 
377  if constexpr(std::is_convertible_v<CurrentT, int64_t>) {
378  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
379  func, static_cast<int64_t>(current), std::forward<Args>(args)...);
380  }
381  } else if(wantedTypeID == AngelScriptTypeIDResolver<int16_t>::Get(this)) {
382 
383  if constexpr(std::is_convertible_v<CurrentT, int16_t>) {
384  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
385  func, static_cast<int16_t>(current), std::forward<Args>(args)...);
386  }
387  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint16_t>::Get(this)) {
388 
389  if constexpr(std::is_convertible_v<CurrentT, uint16_t>) {
390  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
391  func, static_cast<uint16_t>(current), std::forward<Args>(args)...);
392  }
393  } else if(wantedTypeID == AngelScriptTypeIDResolver<int8_t>::Get(this)) {
394 
395  if constexpr(std::is_convertible_v<CurrentT, int8_t>) {
396  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
397  func, static_cast<int8_t>(current), std::forward<Args>(args)...);
398  }
399 
400  } else if(wantedTypeID == AngelScriptTypeIDResolver<uint8_t>::Get(this)) {
401 
402  if constexpr(std::is_convertible_v<CurrentT, uint8_t>) {
403  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
404  func, static_cast<uint8_t>(current), std::forward<Args>(args)...);
405  }
406  } else if(wantedTypeID == AngelScriptTypeIDResolver<float>::Get(this)) {
407 
408  if constexpr(std::is_convertible_v<CurrentT, float>) {
409  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
410  func, static_cast<float>(current), std::forward<Args>(args)...);
411  }
412 
413  } else if(wantedTypeID == AngelScriptTypeIDResolver<double>::Get(this)) {
414 
415  if constexpr(std::is_convertible_v<CurrentT, double>) {
416  return _DoPassEachParameter(parameterc, i, scriptcontext, setup, module,
417  func, static_cast<double>(current), std::forward<Args>(args)...);
418  }
419  }
420 
421  // No conversion possible //
422  return _DoPassParameterTypeError(setup, module, i, wantedTypeID, parameterType);
423  }
424 
425  if constexpr(std::is_same_v<CurrentT, int32_t> || std::is_same_v<CurrentT, uint32_t>) {
426 
427  r = scriptcontext->SetArgDWord(i, current);
428  } else if constexpr(std::is_same_v<CurrentT, int64_t> ||
429  std::is_same_v<CurrentT, uint64_t>) {
430 
431  r = scriptcontext->SetArgQWord(i, current);
432  } else if constexpr(std::is_same_v<CurrentT, int16_t> ||
433  std::is_same_v<CurrentT, uint16_t>) {
434 
435  r = scriptcontext->SetArgWord(i, current);
436  } else if constexpr(std::is_same_v<CurrentT, float>) {
437 
438  r = scriptcontext->SetArgFloat(i, current);
439  } else if constexpr(std::is_same_v<CurrentT, double>) {
440 
441  r = scriptcontext->SetArgDouble(i, current);
442  } else if constexpr(std::is_same_v<CurrentT, char> ||
443  std::is_same_v<CurrentT, int8_t> ||
444  std::is_same_v<CurrentT, bool>) {
445 
446  r = scriptcontext->SetArgByte(i, current);
447 
449  } else if constexpr(std::is_same_v<CurrentT, std::wstring>) {
450 
451  static_assert(!std::is_same_v<CurrentT, std::wstring>,
452  "wstring would have to be saved as a string that can then be passed");
453 
454  } else {
455  // Non-primitive type //
456 
457  // TODO: adding a test to verify this would be nice
458  // Checks for const being used correctly are already done,
459  // so this always does const away cast
460 
461  // Checks for pointers and references to things with type id verification //
462  if constexpr(std::is_pointer_v<CurrentT>) {
463 
465 
466  r = scriptcontext->SetArgAddress(
467  i, const_cast<std::add_pointer_t<
468  std::remove_const_t<std::remove_pointer_t<CurrentT>>>>(current));
469  } else if constexpr(std::is_lvalue_reference_v<CurrentT>) {
470 
472 
473  r = scriptcontext->SetArgAddress(
474  i, &const_cast<std::remove_const_t<std::remove_reference_t<CurrentT>>>(
475  current));
476  } else if constexpr(std::is_class_v<CurrentT>) {
477 
478  // Has to be a class that isn't a handle type
479  // So make sure it isn't
480  static_assert(!std::is_base_of_v<CurrentT, ReferenceCounted>,
481  "Trying to pass an object of reference type by value to script, call new "
482  "on the argument");
483 
484  // If this is by reference, then it must be const
485  if((flags & asTM_OUTREF)) {
486 
487  LOG_ERROR(
488  "ScriptExecutor: script wants to take parameter: " +
489  std::to_string(i) +
490  " as an outref which isn't supported, for func: " + func->GetName());
491  return false;
492 
493  } else if((flags & asTM_INREF) && !(flags & asTM_CONST)) {
494 
495  LOG_ERROR(
496  "ScriptExecutor: script wants to take parameter: " +
497  std::to_string(i) +
498  " as non-const inref which isn't supported (add const), for func: " +
499  func->GetName());
500  return false;
501 
502  } else {
503 
504  r = scriptcontext->SetArgObject(i, const_cast<CurrentT*>(&current));
505  }
506 
507  } else {
508 
509  static_assert(std::is_same_v<CurrentT, void> == std::is_same_v<CurrentT, int>,
510  "Tried to pass some very weird type to a script function");
511  }
512  }
513 
514  // Move to next parameter for the next recursive call //
515  ++i;
516 
517  // Error check //
518  if(r < 0) {
519  LOG_ERROR("ScriptExecutor: failed to pass parameter number: " +
520  std::to_string(i - 1) + ", for func: " + func->GetName());
521  return false;
522  }
523 
524  // Call other parameters //
525  return _DoPassEachParameter(
526  parameterc, i, scriptcontext, setup, module, func, std::forward<Args>(args)...);
527  }
528 
529 
533  template<typename ReturnT>
534  ScriptRunResult<ReturnT> _HandleEndedScriptExecution(int retcode,
535  asIScriptContext* scriptcontext, ScriptRunningSetup& setup, asIScriptFunction* func,
536  ScriptModule* module)
537  {
538  // Check the return type //
539  if(retcode != asEXECUTION_FINISHED) {
540  // something went wrong //
541 
542  // The execution didn't finish as we had planned. Determine why.
543  switch(retcode) {
544  // script caused an exception //
545  case asEXECUTION_EXCEPTION:
546  PrintExceptionInfo(scriptcontext, *Logger::Get(),
547  scriptcontext->GetExceptionFunction(), module);
548  [[fallthrough]];
549  default:
550  // code took too long //
551  case asEXECUTION_ABORTED:
552  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
553  case asEXECUTION_SUSPENDED:
554  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Suspended);
555  }
556  }
557 
558  // Successfully executed, try to fetch return value //
559 
560  const auto returnType = func->GetReturnTypeId();
561 
562  // Script didn't return anything
563  if(returnType == ANGELSCRIPT_VOID_TYPEID) {
564 
565  // Set the return value to default if it isn't void //
566  if constexpr(std::is_void_v<ReturnT>) {
567  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success);
568  } else {
569 
570  if(setup.PrintErrors) {
571 
572  if constexpr(std::is_same_v<asIScriptObject*, ReturnT>) {
573 
574  LOG_ERROR(
575  "ScriptExecutor: script return value is void, but application "
576  "expected a value of type: any script object implementing the "
577  "wanted interface");
578 
579  } else if constexpr(std::is_same_v<asIScriptFunction*, ReturnT>) {
580 
581  LOG_ERROR(
582  "ScriptExecutor: script return value is void, but application "
583  "expected a value of type: any script function matching the "
584  "wanted funcdef");
585 
586  } else if constexpr(CanTypeRepresentAngelScriptTypes<ReturnT>()) {
587  LOG_ERROR(
588  "ScriptExecutor: script return value is void, but application "
589  "expected a value of type: other class that can have many "
590  "angelscript types represented by it");
591  } else {
592  LOG_ERROR(
593  "ScriptExecutor: script return value is void, but application "
594  "expected a value of type: " +
595  std::string(typeid(ReturnT).name()) + " id: " +
596  std::to_string(AngelScriptTypeIDResolver<ReturnT>::Get(this)));
597  }
598 
599  LOG_INFO(
600  "ScriptExecutor: while running function: " +
601  (func ? std::string(func->GetDeclaration()) : setup.Entryfunction));
602  }
603 
604  // Rely on 0 being a valid value for pointer etc.
605  if constexpr(std::is_pointer_v<ReturnT> || !std::is_class_v<ReturnT>) {
606  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success, 0);
607  } else {
608 
609  // Default constructor needs to be available
610  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success, ReturnT());
611  }
612  }
613  }
614 
615  // script return type isn't void //
616  if constexpr(std::is_same_v<ReturnT, void>) {
617 
618  const auto parameterType = AngelScriptTypeIDResolver<ReturnT>::Get(this);
619 
620  // Success, no return value wanted //
621  if(setup.PrintErrors && (returnType != parameterType)) {
622 
623  LOG_WARNING("ScriptExecutor: application ignoring script return value");
624  }
625 
626  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success);
627 
628  } else {
629 
630  if constexpr(CanTypeRepresentAngelScriptTypes<ReturnT>()) {
631 
632  // We are getting a generic asIScriptObject or asIScriptFunction
633  asITypeInfo* info = GetTypeInfo(returnType);
634  const auto flags = info->GetFlags();
635 
636  // Verify type //
637  if constexpr(std::is_same_v<ReturnT, asIScriptObject*>) {
638 
639  if(!(flags & asOBJ_SCRIPT_OBJECT)) {
640 
641  LOG_ERROR("ScriptExecutor: application expected a script object but "
642  "script returned: " +
643  std::string(info->GetName()));
644  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
645  }
646 
647  } else if constexpr(std::is_same_v<ReturnT, asIScriptFunction*>) {
648 
649  if(!(flags & asOBJ_FUNCDEF)) {
650 
651  LOG_ERROR("ScriptExecutor: application expected a script function but "
652  "script returned: " +
653  std::string(info->GetName()));
654  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
655  }
656 
657  } else {
658 
659  LOG_FATAL("Unkown angelscript multi type class");
660  }
661 
662  ReturnT obj = static_cast<ReturnT>(scriptcontext->GetReturnObject());
663 
665  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Success, std::move(obj));
666 
667  } else {
668 
669  const auto parameterType = AngelScriptTypeIDResolver<ReturnT>::Get(this);
670 
671  // TODO: conversions between compatible types
672  if(returnType != parameterType) {
673 
674  return _ReturnedTypeDidntMatch<ReturnT>(
675  scriptcontext, setup, func, module, parameterType, returnType);
676  }
677 
678  if constexpr(std::is_same_v<ReturnT, int32_t> ||
679  std::is_same_v<ReturnT, uint32_t>) {
680 
681  return ScriptRunResult<ReturnT>(
682  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnDWord());
683  } else if constexpr(std::is_same_v<ReturnT, int64_t> ||
684  std::is_same_v<ReturnT, uint64_t>) {
685 
686  return ScriptRunResult<ReturnT>(
687  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnQWord());
688  } else if constexpr(std::is_same_v<ReturnT, float>) {
689 
690  return ScriptRunResult<ReturnT>(
691  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnFloat());
692  } else if constexpr(std::is_same_v<ReturnT, double>) {
693 
694  return ScriptRunResult<ReturnT>(
695  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnDouble());
696  } else if constexpr(std::is_same_v<ReturnT, char> ||
697  std::is_same_v<ReturnT, int8_t> ||
698  std::is_same_v<ReturnT, bool>) {
699 
700  return ScriptRunResult<ReturnT>(
701  SCRIPT_RUN_RESULT::Success, scriptcontext->GetReturnByte());
702  } else {
703 
704  // This is a class type and we need to do a copy if it was
705  // by value or this isn't a handle type
706 
707  // According to AS documentation the return object is
708  // deleted when the context is recycled, so we need to
709  // increase ref or make a copy
710 
711  if constexpr(std::is_pointer_v<ReturnT>) {
712 
713  // We have already done type checks, so this should be fine to cast //
714  ReturnT obj = static_cast<ReturnT>(scriptcontext->GetReturnObject());
715 
717  return ScriptRunResult<ReturnT>(
718  SCRIPT_RUN_RESULT::Success, std::move(obj));
719 
720  } else if constexpr(std::is_lvalue_reference_v<ReturnT>) {
721 
722  static_assert(!std::is_class_v<ReturnT>,
723  "Returning by reference from scripts doesn't work");
724 
725  } else if constexpr(std::is_class_v<ReturnT>) {
726 
727  // We have already done type checks, so this should be fine to cast //
728  ReturnT* obj = static_cast<ReturnT*>(scriptcontext->GetReturnObject());
729  return ScriptRunResult<ReturnT>(
730  SCRIPT_RUN_RESULT::Success, std::move(*obj));
731  } else {
732 
733  static_assert(
734  std::is_same_v<ReturnT, void> == std::is_same_v<ReturnT, int>,
735  "Tried to return some very weird type from a script function");
736  }
737  }
738  }
739  }
740  }
741 
749  template<class... Args>
750  bool _PassParametersToScript(asIScriptContext* scriptcontext, ScriptRunningSetup& setup,
751  ScriptModule* module, asIScriptFunction* func, Args&&... args)
752  {
753  // Get the number of parameters expected //
754  auto parameterc = func->GetParamCount();
755  asUINT i = 0;
756 
757  // Start passing the parameters provided by the application //
758  if(!_DoPassEachParameter(
759  parameterc, i, scriptcontext, setup, module, func, std::forward<Args>(args)...))
760  return false;
761 
762  // Check that we passed enough parameters for the script (we
763  // don't care if the script took less parameters than we gave
764  // it)
765  if(i < parameterc) {
766  // We didn't have enough parameters
767  if(setup.PrintErrors) {
768  LOG_ERROR("ScriptExecutor: not enough parameters to pass to script function");
769  }
770 
771  return false;
772  }
773 
774  return true;
775  }
776 
777  // End condition for the variadic template
778  bool _DoPassEachParameter(asUINT parameterc, asUINT& i, asIScriptContext* scriptcontext,
779  ScriptRunningSetup& setup, ScriptModule* module, asIScriptFunction* func)
780  {
781  return true;
782  }
783 
785  template<typename ReturnT>
786  ScriptRunResult<ReturnT> _ReturnedTypeDidntMatch(asIScriptContext* scriptcontext,
787  ScriptRunningSetup& setup, asIScriptFunction* func, ScriptModule* module,
788  int parameterType, int returnType)
789  {
790  _DoReceiveParameterTypeError(setup, module, parameterType, returnType);
791  return ScriptRunResult<ReturnT>(SCRIPT_RUN_RESULT::Error);
792  }
793 
799  DLLEXPORT bool _DoPassParameterTypeError(ScriptRunningSetup& setup, ScriptModule* module,
800  int i, int scriptwanted, int provided);
801 
804  DLLEXPORT void _DoReceiveParameterTypeError(
805  ScriptRunningSetup& setup, ScriptModule* module, int applicationwanted, int scripthad);
806 
807 
808 
812  void _CallBehaviourReleaseIfNeeded(void* obj, int returnTypeID)
813  {
814  // TODO: this should instead call engine->ReleaseScriptObject
815  asITypeInfo* info = GetASEngine()->GetTypeInfoById(returnTypeID);
816 
817  const auto flags = info->GetFlags();
818  if((flags & asOBJ_REF) && !(flags & asOBJ_NOCOUNT)) {
819 
820  LOG_INFO("ScriptExecutor: attempting to release ref through angelscript");
821 
822  RunReleaseRefOnObject(obj, returnTypeID);
823 
824  LOG_INFO("Success calling behaviour release");
825  }
826  }
827 
828 
829  // ------------------------------------ //
830 
832  DLLEXPORT bool _CheckScriptFunctionPtr(
833  asIScriptFunction* func, ScriptRunningSetup& parameters, ScriptModule* scrptmodule);
834 
836  DLLEXPORT bool _PrepareContextForPassingParameters(asIScriptFunction* func,
837  asIScriptContext* ScriptContext, ScriptRunningSetup& parameters,
838  ScriptModule* scriptmodule);
839 
840 protected:
843  DLLEXPORT asIScriptContext* _GetContextForExecution();
844 
847  DLLEXPORT void _DoneWithContext(asIScriptContext* context);
848 
849 private:
850  // AngelScript engine script executing part //
851  asIScriptEngine* engine;
852 
853  // list of modules that have been created, some might only have this as reference, and
854  // could potentially be released
855  std::vector<std::shared_ptr<ScriptModule>> AllocatedScriptModules;
856 
857  Mutex ModulesLock;
858 
860  std::vector<asIScriptContext*> ContextPool;
861 
863  Mutex ContextPoolLock;
864 
865  static ScriptExecutor* instance;
866 };
867 
868 } // namespace Leviathan
DLLEXPORT std::string GetTypeName(int type) const
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:90
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:92
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:94
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:91
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