//---------------------------------------------------------------------------------------
// Execute a blueprint event
void SkUEBlueprintInterface::mthd_trigger_event(SkInvokedMethod * scope_p, SkInstance ** result_pp)
  {
  const EventEntry & event_entry = static_cast<const EventEntry &>(*ms_singleton_p->m_binding_entry_array[scope_p->get_invokable()->get_user_data()]);
  SK_ASSERTX(event_entry.m_type == BindingType_Event, "BindingEntry has bad type!");

  // Create parameters on stack
  const K2ParamEntry * param_entry_array = event_entry.get_param_entry_array();
  UFunction * ue_function_p = event_entry.m_ue_function_p.Get(); // Invoke the first one
  uint8_t * k2_params_storage_p = a_stack_allocate(ue_function_p->ParmsSize, uint8_t);
  for (uint32_t i = 0; i < event_entry.m_num_params; ++i)
    {
    const K2ParamEntry & param_entry = param_entry_array[i];
    (*param_entry.m_getter_p)(k2_params_storage_p + param_entry.m_offset, scope_p->get_arg(i));
    }

  // Invoke K2 script event with parameters
  AActor * actor_p = scope_p->this_as<SkUEActor>();
  if (!event_entry.m_ue_function_to_invoke_p.IsValid())
    {
    // Find Kismet copy of our method to invoke
    event_entry.m_ue_function_to_invoke_p = actor_p->FindFunctionChecked(*ue_function_p->GetName());
    }
  // Check if this event is actually present in any Blueprint graph
  SK_ASSERTX(event_entry.m_ue_function_to_invoke_p->Script.Num() > 0, a_str_format("Warning: Call to %S has no effect as no such Blueprint event node exists.", *ue_function_p->GetName()));
  actor_p->ProcessEvent(event_entry.m_ue_function_to_invoke_p.Get(), k2_params_storage_p);

  // No return value
  if (result_pp) *result_pp = SkBrain::ms_nil_p;
  return;
  }
//---------------------------------------------------------------------------------------
// One-time shutdown of SkookumScript
void SkUERuntime::shutdown()
  {
  SK_ASSERTX(!SkookumScript::is_flag_set(SkookumScript::Flag_updating), "Attempting to shut down SkookumScript while it is in the middle of an update.");
  SK_ASSERTX(m_is_initialized, "Tried to shut down SkUERuntime without prior initialization.");

  // Printing during shutdown will re-launch IDE in case it has been closed prior to UE4
  // So quick fix is to just not print during shutdown
  //A_DPRINT("\nSkookumScript shutting down.\n");

  #ifdef SKOOKUM_REMOTE_UNREAL
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Disconnect from remote client
    SkookumRemoteBase::ms_default_p->set_mode(SkLocale_embedded);
  #endif

  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // Clears out Blueprint interface mappings
  SkUEBlueprintInterface::get()->clear();

  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // Unloads SkookumScript and cleans-up
  SkookumScript::deinitialize_session();
  SkookumScript::deinitialize();

  m_is_initialized = false;
  }
//---------------------------------------------------------------------------------------
void USkookumScriptComponent::create_sk_instance()
  {
  SK_ASSERTX(!m_instance_p, "Tried to create instance when instance already present!");

  // Find the actor I belong to
  AActor * actor_p = GetOwner();
  SK_ASSERTX(actor_p, "USkookumScriptComponent must be attached to an actor.");

  // Determine SkookumScript class of my actor
  SkClass * class_p = nullptr;
  FString class_name = ScriptClassName;
  if (!class_name.IsEmpty())
    {
    AString class_name_ascii(*class_name, class_name.Len());
    class_p = SkBrain::get_class(class_name_ascii.as_cstr());
    SK_ASSERTX(class_p, a_cstr_format("Cannot find Script Class Name '%s' specified in SkookumScriptComponent of '%S'. Misspelled?", class_name_ascii.as_cstr(), *actor_p->GetName()));
    if (!class_p) goto set_default_class; // Recover from bad user input

    // Do some extra checking in non-shipping builds
    #if (SKOOKUM & SK_DEBUG)
      // Find most derived SkookumScript class known to UE4
      SkClass * known_super_class_p;
      for (known_super_class_p = class_p; known_super_class_p; known_super_class_p = known_super_class_p->get_superclass())
        {
        if (SkUEClassBindingHelper::get_ue_class_from_sk_class(known_super_class_p)) break;
        }

      // Find most derived UE4 class known to SkookumScript
      SkClass * mapped_class_p = nullptr;
      for (UClass * obj_uclass_p = actor_p->GetClass(); !mapped_class_p && obj_uclass_p; obj_uclass_p = obj_uclass_p->GetSuperClass())
        {
        mapped_class_p = SkUEClassBindingHelper::get_sk_class_from_ue_class(obj_uclass_p);
        }
      SK_ASSERTX(mapped_class_p && mapped_class_p == known_super_class_p, a_cstr_format("Script Class Name '%s' in SkookumScriptComponent of '%S' is not properly related to Actor. Both the Script Class Name '%s' and the UE4 class of '%S' ('%S') must share the topmost ancestor class known to both SkookumScript and UE4. Right now these ancestor classes are different ('%s' for '%s' and '%s' for '%S').", class_name_ascii.as_cstr(), *actor_p->GetName(), class_name_ascii.as_cstr(), *actor_p->GetName(), *actor_p->GetClass()->GetName(), known_super_class_p ? known_super_class_p->get_name_cstr_dbg() : "<none>", class_name_ascii.as_cstr(), mapped_class_p ? mapped_class_p->get_name_cstr_dbg() : "<none>", *actor_p->GetClass()->GetName()));
    #endif
    }
  else
    {
  set_default_class:
    // Find most derived UE4 class known to SkookumScript
    class_p = nullptr; // Is already null when we get here, but set again for clarity
    for (UClass * obj_uclass_p = actor_p->GetClass(); !class_p && obj_uclass_p; obj_uclass_p = obj_uclass_p->GetSuperClass())
      {
      class_p = SkUEClassBindingHelper::get_sk_class_from_ue_class(obj_uclass_p);
      }
    SK_ASSERTX(class_p, a_cstr_format("No parent class of %S is known to SkookumScript!", *actor_p->GetClass()->GetName()));
    if (!class_p)
      {
      class_p = SkBrain::get_class(ASymbol_Actor); // Recover from bad user input
      }
    }

  // Based on the desired class, create SkInstance or SkDataInstance
  SkInstance * instance_p = class_p->new_instance();
  instance_p->construct<SkUEActor>(actor_p); // Keep track of owner actor
  m_instance_p = instance_p;
  }
void USkookumScriptListener::push_event_and_resume(EventInfo * event_p, uint32_t num_arguments)
  {
  #if (SKOOKUM & SK_DEBUG)
    for (uint32_t i = 0; i < num_arguments; ++i) SK_ASSERTX(event_p->m_argument_p[i], "All event arguments must be set.");
    for (uint32_t i = num_arguments; i < A_COUNT_OF(event_p->m_argument_p); ++i) SK_ASSERTX(!event_p->m_argument_p[i], "Unused event arguments must be left alone.");
    SK_ASSERTX(m_num_arguments == 0 || m_num_arguments == num_arguments, "All events must have same argument count.");
  #endif
  m_num_arguments = num_arguments;
  m_event_queue.append(event_p);
  if (m_coro_p.is_valid()) m_coro_p->resume();
  }
void SkUEBlueprintInterface::exec_coroutine(FFrame & stack, void * const result_p)
  {
  const FunctionEntry & function_entry = static_cast<const FunctionEntry &>(*ms_singleton_p->m_binding_entry_array[stack.CurrentNativeFunction->RepOffset]);
  SK_ASSERTX(function_entry.m_type == BindingType_Function, "BindingEntry has bad type!");
  SK_ASSERTX(function_entry.m_sk_invokable_p->get_invoke_type() == SkInvokable_coroutine, "Must not be coroutine at this point.");

  // Create invoked coroutine
  SkInvokedCoroutine * icoroutine_p = SkInvokedCoroutine::pool_new(static_cast<SkCoroutine *>(function_entry.m_sk_invokable_p));

  // Get instance of this object
  SkInstance * this_p = SkUEEntity::new_instance(stack.Object);

  // Set parameters
  icoroutine_p->reset(SkCall_interval_always, nullptr, this_p, nullptr, nullptr);

  #if defined(SKDEBUG_COMMON)
    // Set with SKDEBUG_ICALL_STORE_GEXPR stored here before calls to argument expressions
    // overwrite it.
    const SkExpressionBase * call_expr_p = SkInvokedContextBase::ms_last_expr_p;
  #endif

  SKDEBUG_ICALL_SET_EXPR(icoroutine_p, call_expr_p);

  // Fill invoked coroutine's argument list
  const SkParamEntry * param_entry_array = function_entry.get_param_entry_array();
  icoroutine_p->data_ensure_size(function_entry.m_num_params);
  for (uint32_t i = 0; i < function_entry.m_num_params; ++i)
    {
    const SkParamEntry & param_entry = param_entry_array[i];
    icoroutine_p->data_append_arg((*param_entry.m_fetcher_p)(stack));
    }

  // Done with stack - now increment the code ptr unless it is null
  stack.Code += !!stack.Code;

  SKDEBUG_HOOK_EXPR(call_expr_p, icoroutine_p, nullptr);

  #if (SKOOKUM & SK_DEBUG)
    if (!this_p->get_class()->is_class(*function_entry.m_sk_class_p))
      {
      SK_ERRORX(a_str_format("Attempted to invoke coroutine '%s@%s' via a blueprint of type '%s'. You might have forgotten to specify the SkookumScript type of this blueprint as '%s' in its SkookumScript component.", function_entry.m_sk_class_p->get_name_cstr(), function_entry.m_invokable_name.as_cstr(), this_p->get_class()->get_name_cstr(), function_entry.m_sk_class_p->get_name_cstr()));
      }
    else
  #endif
      {
      // Invoke the coroutine on this_p - might return immediately
      icoroutine_p->on_update();
      }

  // Free if not in use by our invoked coroutine
  this_p->dereference();
  }
//---------------------------------------------------------------------------------------
void SkUERemote::get_project_info(SkProjectInfo * out_project_info_p)
  {
  // Get platform id string
  out_project_info_p->m_platform_id = FStringToAString(UGameplayStatics::GetPlatformName());

  // Get engine id string
  out_project_info_p->m_engine_id.ensure_size(20);
  out_project_info_p->m_engine_id.format("UE%d.%d.%d-%s", ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION, ENGINE_PATCH_VERSION, BUILT_FROM_CHANGELIST ? "Installed" : "Compiled");

  // Get game name
  out_project_info_p->m_project_name = FStringToAString(FApp::GetGameName());

  // Name of generated scripts overlay
  TCHAR const * const generated_overlay_name_p = TEXT("Project-Generated");

  // Look for default SkookumScript project file in engine folder.
  FString default_project_path(FPaths::EnginePluginsDir() / TEXT("SkookumScript/Scripts/Skookum-project-default.ini"));
  SK_ASSERTX(FPaths::FileExists(default_project_path), a_str_format("Cannot find default project settings file '%S'!", *default_project_path));
  out_project_info_p->m_default_project_path = FStringToAString(FPaths::ConvertRelativePathToFull(default_project_path));

  // Check if we have loaded any game
  if (!out_project_info_p->m_project_name.is_empty())
    {
    // Look for specific SkookumScript project in game/project folder.
    // 1) Check permanent location
    FString project_path(FPaths::GameDir() / TEXT("Scripts/Skookum-project.ini"));
    if (FPaths::FileExists(project_path))
      {
      #if WITH_EDITORONLY_DATA
        if (m_editor_interface_p)
          {
          m_editor_interface_p->set_overlay_path(FPaths::GetPath(project_path), generated_overlay_name_p);
          }
      #endif
      }
    else
      {
      project_path.Empty();
      #if WITH_EDITORONLY_DATA
        if (m_editor_interface_p)
          {
          // 2) Check/create temp location
          project_path = m_editor_interface_p->ensure_temp_project(generated_overlay_name_p);
          SK_ASSERTX(!project_path.IsEmpty(), a_str_format("Could not generated project file '%S' for project '%s'!", *project_path, out_project_info_p->m_project_name.as_cstr()));
          }
      #endif
      }

    out_project_info_p->m_project_path = FStringToAString(FPaths::ConvertRelativePathToFull(project_path));
    }
  }
void SkUEBlueprintInterface::delete_binding_entry(uint32_t binding_index)
  {
  BindingEntry * binding_entry_p = m_binding_entry_array[binding_index];
  if (binding_entry_p)
    {
    SK_ASSERTX(binding_entry_p->m_ue_function_p.IsValid() || !binding_entry_p->m_ue_class_p.IsValid(), a_str_format("UFunction %s was deleted outside of SkUEBlueprintInterface and left dangling links behind in its owner UClass (%S).", binding_entry_p->m_invokable_name.as_cstr(), *binding_entry_p->m_ue_class_p->GetName()));
    if (binding_entry_p->m_ue_function_p.IsValid())
      {
      UFunction * ue_function_p = binding_entry_p->m_ue_function_p.Get();
      UClass * ue_class_p = binding_entry_p->m_ue_class_p.Get();
      // Unlink from its owner class
      ue_class_p->RemoveFunctionFromFunctionMap(ue_function_p);
      // Unlink from the Children list as well
      UField ** prev_field_pp = &ue_class_p->Children;
      for (UField * field_p = *prev_field_pp; field_p; prev_field_pp = &field_p->Next, field_p = *prev_field_pp)
        {
        if (field_p == ue_function_p)
          {
          *prev_field_pp = field_p->Next;
          break;
          }
        }
      // Destroy the function along with its attached properties
      ue_function_p->ConditionalBeginDestroy();
      }
    AMemory::free(binding_entry_p);
    m_binding_entry_array.set_at(binding_index, nullptr);
    }
  }
//---------------------------------------------------------------------------------------
void USkookumScriptComponent::delete_sk_instance()
  {
  SK_ASSERTX(m_instance_p, "No Sk instance to delete!");
  m_instance_p->clear_coroutines();
  m_instance_p->dereference();
  m_instance_p = nullptr;
  }
 static void assert_actor_has_overlap_events_enabled(AActor * actor_p)
   {
   // Check that events will properly fire
   TArray<UActorComponent *> components = actor_p->GetComponentsByClass(UPrimitiveComponent::StaticClass());
   SK_ASSERTX(components.Num() > 0, a_cstr_format("Trying to receive overlap events on actor '%S' but it has no primitive (collision) component.", *actor_p->GetName()));
   bool found_enabled_overlap_event = false;
   for (UActorComponent * component_p : components)
     {
     if (Cast<UPrimitiveComponent>(component_p)->bGenerateOverlapEvents)
       {
       found_enabled_overlap_event = true;
       break;
       }
     }
   SK_ASSERTX(found_enabled_overlap_event, a_cstr_format("Trying to receive overlap events on actor '%S' but it has no primitive component that has overlap events turned on. To fix this, check the box 'Generate Overlap Events' for the primitive component (e.g. SkeletalMeshComponent, CapsuleComponent etc.) that you would like to trigger the overlap events. You might also simply have picked the wrong actor.", *actor_p->GetName()));
   }
void SkUEBlueprintInterface::bind_event_method(SkMethodBase * sk_method_p)
  {
  SK_ASSERTX(!sk_method_p->is_bound() || static_cast<SkMethodFunc *>(sk_method_p)->m_atomic_f == &mthd_trigger_event, a_str_format("Trying to bind Blueprint event method '%s' but it is already bound to a different atomic implementation!", sk_method_p->get_name_cstr_dbg()));
  if (!sk_method_p->is_bound())
    {
    sk_method_p->get_scope()->register_method_func(sk_method_p->get_name(), &mthd_trigger_event, SkBindFlag_instance_no_rebind);
    }
  }
bool USkookumScriptListener::coro_wait_event(SkInvokedCoroutine * scope_p, tUnregisterCallback register_f, tUnregisterCallback unregister_f)
  {
  UObject * this_p = scope_p->this_as<SkUEEntity>();

  SK_ASSERTX(this_p, a_str_format("Tried to wait for an event on an Entity of type '%s' but it is null!", scope_p->get_this()->get_class()->get_name_cstr()));
  // If this_p is null, we can't listen for events so return immediately
  if (!this_p) return true;

  // Just started?
  if (scope_p->m_update_count == 0u)
    {
    // If this is null, treat it as if there's nothing to do
    if (!this_p) return true;

    // Install and store away event listener
    USkookumScriptListener * listener_p = SkookumScriptListenerManager::get_singleton()->alloc_listener(this_p, scope_p, unregister_f);
    scope_p->append_user_data<FSkookumScriptListenerAutoPtr, USkookumScriptListener *>(listener_p);
    (*register_f)(this_p, listener_p);

    // Suspend coroutine
    scope_p->suspend();

    // Coroutine not complete yet - call again when resumed
    return false;
    }

  // Get back stored event listener
  USkookumScriptListener * listener_p = scope_p->get_user_data<FSkookumScriptListenerAutoPtr>()->Get();
  SK_ASSERTX(listener_p->has_event(), "Must have event at this point as coroutine was resumed by delegate object.");

  // Return first event queued up on listener
  // and *DISCARD* potential other events
  SkClosure * closure_p = scope_p->get_arg_data<SkClosure>(SkArg_1);
  uint32_t num_arguments = listener_p->get_num_arguments();
  bool exit = false;
  USkookumScriptListener::EventInfo * event_p = listener_p->pop_event();
  for (uint32_t i = 0; i < num_arguments; ++i)
    {
    scope_p->set_arg(SkArg_1 + i, event_p->m_argument_p[i]); // Store parameters as return values if exiting
    }
  listener_p->free_event(event_p, false);

  // Ok done, return event parameters and quit
  return true;
  }
SkUEBlueprintInterface::SkUEBlueprintInterface()
  {
  SK_ASSERTX(!ms_singleton_p, "There can be only one instance of this class.");
  ms_singleton_p = this;

  m_struct_vector3_p          = FindObjectChecked<UScriptStruct>(UObject::StaticClass()->GetOutermost(), TEXT("Vector"), false);
  m_struct_rotation_angles_p  = FindObjectChecked<UScriptStruct>(UObject::StaticClass()->GetOutermost(), TEXT("Rotator"), false);
  m_struct_transform_p        = FindObjectChecked<UScriptStruct>(UObject::StaticClass()->GetOutermost(), TEXT("Transform"), false);
  }
void USkookumScriptListener::initialize(UObject * obj_p, SkInvokedCoroutine * coro_p, tUnregisterCallback callback_p)
  {
  SK_ASSERTX(!coro_p->is_suspended(), "Coroutine must not be suspended yet when delegate object is initialized.");

  m_obj_p = obj_p;
  m_coro_p = coro_p;
  m_unregister_callback_p = callback_p;
  m_num_arguments = 0;
  }
//---------------------------------------------------------------------------------------
// Resolve the raw data info of each raw data member of the given class
void SkUEClassBindingHelper::resolve_raw_data(SkClass * class_p)
  {
  // By default, inherit raw pointer and accessor functions from super class
  SkClass * super_class_p = class_p->get_superclass();
  if (super_class_p)
    {
    if (!class_p->get_raw_pointer_func())
      {
      class_p->register_raw_pointer_func(super_class_p->get_raw_pointer_func());
      }
    if (!class_p->get_raw_accessor_func())
      {
      class_p->register_raw_accessor_func(super_class_p->get_raw_accessor_func());
      }
    }

  // First check if it's a class
  UStruct * ue_struct_or_class_p = get_ue_class_from_sk_class(class_p);
  if (!ue_struct_or_class_p)
    {
    // Not a class, must be a struct then
    ue_struct_or_class_p = get_static_ue_struct_from_sk_class(class_p);
    }

  if (ue_struct_or_class_p)
    {
    // Resolve raw data
    resolve_raw_data(class_p, ue_struct_or_class_p);
    }
  else
    {
    // In cooked builds, don't bother as unused classes might have been optimized out
    #if WITH_EDITORONLY_DATA

      // Potentially report error
      tSkTypedNameRawArray & raw_data = class_p->get_instance_data_raw_for_resolving();
      if (!raw_data.is_empty())
        {
        // Check if maybe all variables are already resolved
        bool all_resolved = true;
        for (auto var_p : raw_data)
          {
          if (var_p->m_raw_data_info == SkRawDataInfo_Invalid)
            {
            all_resolved = false;
            break;
            }
          }

        // In commandlet mode, SkookumScript code is never run
        // If all resolved already, no problem either
        SK_ASSERTX(all_resolved || IsRunningCommandlet(), a_str_format("Class '%s' has raw data but no known class mapping to UE4 for resolving.", class_p->get_name_cstr_dbg()));
        }

    #endif
    }
  }
void SkUEBlueprintInterface::exec_method(FFrame & stack, void * const result_p, SkInstance * this_p)
  {
  const FunctionEntry & function_entry = static_cast<const FunctionEntry &>(*ms_singleton_p->m_binding_entry_array[stack.CurrentNativeFunction->RepOffset]);
  SK_ASSERTX(function_entry.m_type == BindingType_Function, "BindingEntry has bad type!");
  SK_ASSERTX(function_entry.m_sk_invokable_p->get_invoke_type() == SkInvokable_method, "Must not be coroutine or atomic at this point.");

  SkInvokedMethod imethod(nullptr, this_p, static_cast<SkMethodBase *>(function_entry.m_sk_invokable_p), a_stack_allocate(function_entry.m_sk_invokable_p->get_invoked_data_array_size(), SkInstance*));

  SKDEBUG_ICALL_SET_INTERNAL(&imethod);
  SKDEBUG_HOOK_SCRIPT_ENTRY(function_entry.m_invokable_name);

  // Fill invoked method's argument list
  const SkParamEntry * param_entry_array = function_entry.get_param_entry_array();
  imethod.data_ensure_size(function_entry.m_num_params);
  for (uint32_t i = 0; i < function_entry.m_num_params; ++i)
    {
    const SkParamEntry & param_entry = param_entry_array[i];
    imethod.data_append_arg((*param_entry.m_fetcher_p)(stack));
    }

  // Done with stack - now increment the code ptr unless it is null
  stack.Code += !!stack.Code;

  #if (SKOOKUM & SK_DEBUG)
    if (!this_p->get_class()->is_class(*function_entry.m_sk_class_p))
      {
      SK_ERRORX(a_str_format("Attempted to invoke method '%s@%s' via a blueprint of type '%s'. You might have forgotten to specify the SkookumScript type of this blueprint as '%s' in its SkookumScript component.", function_entry.m_sk_class_p->get_name_cstr(), function_entry.m_invokable_name.as_cstr(), this_p->get_class()->get_name_cstr(), function_entry.m_sk_class_p->get_name_cstr()));
      }
    else
  #endif
      {
      // Call method
      SkInstance * result_instance_p = SkBrain::ms_nil_p;
      static_cast<SkMethod *>(function_entry.m_sk_invokable_p)->SkMethod::invoke(&imethod, nullptr, &result_instance_p); // We know it's a method so call directly
      if (function_entry.m_result_getter)
        {
        (*function_entry.m_result_getter)(result_p, result_instance_p);
        }
      }

  SKDEBUG_HOOK_SCRIPT_EXIT();
  }
 //---------------------------------------------------------------------------------------
 // Get Unreal Engine class of this class
 static void mthdc_static_class(SkInvokedMethod * scope_p, SkInstance ** result_pp)
   {
   if (result_pp) // Do nothing if result not desired
     {
     // Determine class
     SkClass * sk_class_p = ((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
     UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(sk_class_p);
     SK_ASSERTX(ue_class_p, a_cstr_format("The UE4 equivalent of class type '%s' is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", sk_class_p->get_name_cstr_dbg()));
     *result_pp = ue_class_p ? SkUEEntityClass::new_instance(ue_class_p) : SkBrain::ms_nil_p;
     }
   }
  //---------------------------------------------------------------------------------------
  // Helper function for various from methods below
  static bool setup_from(SkInvokedMethod * scope_p, AActor ** actor_pp, SkClass ** sk_class_pp, SkClass ** known_sk_class_pp, UClass ** ue_class_pp)
    {
    // Class we are looking for
    SkClass * sk_class_p = *sk_class_pp = ((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();

    // Actor we are getting the component(s) from 
    AActor * actor_p = *actor_pp = scope_p->get_arg<SkUEActor>(SkArg_1);
    SK_ASSERTX(actor_p, a_str_format("Failed to get a '%s' from null actor.", sk_class_p->get_name_cstr_dbg()));

    // Figure out which UE4 class to look for
    bool is_class_valid = true;
    if (actor_p)
      {
      SkClass * known_sk_class_p = *known_sk_class_pp = SkUEClassBindingHelper::find_most_derived_super_class_known_to_ue(sk_class_p, ue_class_pp);
      is_class_valid = sk_class_p->is_component_class() || known_sk_class_p == sk_class_p;
      SK_ASSERTX(is_class_valid, a_str_format("Failed to get a '%s' from actor '%S'. Make sure that '%s' is either a class known to UE4 (a Blueprint class or an exposed UCLASS), or it is erived from SkookumScriptBehaviorComponent.", sk_class_p->get_name_cstr_dbg(), *actor_p->GetName(), sk_class_p->get_name_cstr_dbg()));
      }

    return actor_p && is_class_valid;
    }
//---------------------------------------------------------------------------------------
// Get pointer to UWorld from global variable
UWorld * SkUEClassBindingHelper::get_world()
  {
  if (!SkookumScript::is_flag_set(SkookumScript::Flag_evaluate))
    {
    return nullptr;
    }

  SkInstance * world_var_p = SkBrain::ms_object_class_p->get_class_data_value_by_idx(get_world_data_idx());
  SK_ASSERTX(world_var_p && (world_var_p == SkBrain::ms_nil_p || world_var_p->get_class() == SkBrain::get_class(ASymbol_World)), "@@world variable does not have proper type."); // nil is ok
  return world_var_p == SkBrain::ms_nil_p ? nullptr : world_var_p->as<SkUEWorld>();
  }
//---------------------------------------------------------------------------------------
void USkookumScriptComponent::UninitializeComponent()
  {
  // Call SkookumScript destructor, but only if we are located inside the game world
  if (m_instance_p && GetOwner()->GetWorld() == SkUEClassBindingHelper::get_world())
    {
    SK_ASSERTX(SkookumScript::is_flag_set(SkookumScript::Flag_evaluate), "SkookumScript must be in initialized state when UninitializeComponent() is invoked.");
    delete_sk_instance();
    }

  Super::UninitializeComponent();
  }
//---------------------------------------------------------------------------------------
void USkookumScriptComponent::InitializeComponent()
  {
  Super::InitializeComponent();

  // Call SkookumScript constructor, but only if we are located inside the game world
  if (GetOwner()->GetWorld() == SkUEClassBindingHelper::get_world())
    {
    SK_ASSERTX(SkookumScript::is_flag_set(SkookumScript::Flag_evaluate), "SkookumScript must be in initialized state when InitializeComponent() is invoked.");
    create_sk_instance();
    m_instance_p->call_default_constructor();
    }
  }
//---------------------------------------------------------------------------------------
// 
bool FSkookumScriptRuntime::has_skookum_default_constructor(UClass * class_p) const
  {
  SK_ASSERTX(m_runtime.is_initialized(), "Runtime must be initialized for this code to work.");

  SkClass * sk_class_p = SkUEClassBindingHelper::get_sk_class_from_ue_class(class_p);
  if (sk_class_p)
    {
    return (sk_class_p->find_instance_method_inherited(ASymbolX_ctor) != nullptr);
    }

  return false;
  }
//---------------------------------------------------------------------------------------
// Compute data idx to world variable
int32_t SkUEClassBindingHelper::get_world_data_idx()
  {
  if (ms_world_data_idx < 0)
    {
    if (!SkBrain::ms_object_class_p->get_class_data().find(ASymbolX_c_world, AMatch_first_found, (uint32_t*)&ms_world_data_idx))
      {
      SK_ASSERTX(false, "Couldn't find the @@world class member variable!");
      }
    }

  return ms_world_data_idx;
  }
//---------------------------------------------------------------------------------------
void USkookumScriptComponent::UninitializeComponent()
  {
  Super::UninitializeComponent();

  // Call SkookumScript destructor, but only if we are located inside the game world
  if (GetOwner()->GetWorld() == SkUEClassBindingHelper::get_world())
    {
    //SkDebug::print_ide(a_str_format("USkookumScriptComponent::UninitializeComponent() 0x%p\n", this), SkLocale_ide, SkDPrintType_trace);
    SK_ASSERTX(SkookumScript::is_flag_set(SkookumScript::Flag_evaluate), "SkookumScript must be in initialized state when UninitializeComponent() is invoked.");
    m_instance_p->call_destructor();
    }
  }
  //---------------------------------------------------------------------------------------
  // !new constructor - creates new object
  void mthd_ctor_new(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    UObject * outer_p = scope_p->get_arg<SkUEEntity>(SkArg_1);
    FName name = scope_p->get_arg<SkUEName>(SkArg_2);
    uint32_t flags = scope_p->get_arg<SkInteger>(SkArg_3);
    
    // The scope of a constructor is always some form of an SkInstance
    SkInstance * receiver_p = static_cast<SkInstance *>(scope_p->m_scope_p.get_obj());

    UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(receiver_p->get_class());
    SK_ASSERTX(ue_class_p, a_cstr_format("The UE4 equivalent of class type '%s' is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", receiver_p->get_class()->get_name_cstr_dbg()));
    scope_p->get_this()->construct<SkUEEntity>(NewObject<UObject>(outer_p, ue_class_p, name, EObjectFlags(flags)));
    }
//---------------------------------------------------------------------------------------
// One-time initialization of SkookumScript
// See Also    shutdown()
// Author(s)   Conan Reis
void SkUERuntime::startup()
  {
  SK_ASSERTX(!m_is_initialized, "Tried to initialize SkUERuntime twice in a row.");

  A_DPRINT("\nSkookumScript starting up.\n");

  // Let scripting system know that the game engine is present and is being hooked-in
  SkDebug::enable_engine_present();

  #ifdef SKOOKUM_REMOTE_UNREAL
    SkDebug::register_print_with_agog();
  #endif

  SkBrain::register_bind_atomics_func(SkookumRuntimeBase::bind_routines);
  SkClass::register_raw_resolve_func(SkUEClassBindingHelper::resolve_raw_data);

  m_is_initialized = true;
  }
//---------------------------------------------------------------------------------------
// 
void FSkookumScriptRuntime::tick_remote()
  {
  if (!IsRunningCommandlet())
    {
    // Request recompilation of binaries if script files changed
    if (m_freshen_binaries_requested)
      {
      m_remote_client.cmd_compiled_state(true);
      m_freshen_binaries_requested = false;
      }

    // Remote communication to and from SkookumScript IDE.
    // Needs to be called whether in editor or game and whether paused or not
    // $Revisit - CReis This is probably a hack. The remote client update should probably
    // live somewhere other than a tick method such as its own thread.
    m_remote_client.process_incoming();

    // Re-load compiled binaries?
    if (m_remote_client.is_load_compiled_binaries_requested())
      {
      // Load the Skookum class hierarchy scripts in compiled binary form
      #if WITH_EDITOR
        bool is_first_time = !is_skookum_initialized();
      #endif

      bool success_b = m_runtime.load_compiled_scripts();
      SK_ASSERTX(success_b, AErrMsg("Unable to load SkookumScript compiled binaries!", AErrLevel_notify));
      m_remote_client.clear_load_compiled_binaries_requested();

      #if WITH_EDITOR
        if (is_first_time && is_skookum_initialized())
          {
          // When we load the binaries for the very first time, try regenerating all generated class script files again,
          // as the editor might have tried to generate them before but skipped because SkookumScript was not initialized yet
          m_runtime.get_editor_interface()->generate_all_class_script_files();
          // Also recompile Blueprints in error state as such error state might have been due to SkookumScript not being initialized at the time of compile
          m_runtime.get_editor_interface()->recompile_blueprints_with_errors();
          // Set world pointer now
          SkUEClassBindingHelper::set_world(m_game_world_p);
          }
      #endif
      }
    }
  }
  //---------------------------------------------------------------------------------------
  // Entity@default() ThisClass_
  static void mthdc_default(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    if (result_pp) // Do nothing if result not desired
      {
      // Determine class of object to get
      SkClass * sk_class_p = ((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
      UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(sk_class_p);
      SK_ASSERTX(ue_class_p, a_cstr_format("Cannot get default instance of class '%s' as the UE4 equivalent of this class is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", sk_class_p->get_name_cstr_dbg()));

      // Get default object
      UObject * obj_p = nullptr;
      if (ue_class_p)
        {
        obj_p = GetMutableDefault<UObject>(ue_class_p);
        }

      *result_pp = obj_p ? SkUEEntity::new_instance(obj_p, ue_class_p, sk_class_p) : SkBrain::ms_nil_p;
      }
    }
  //---------------------------------------------------------------------------------------
  // Entity@load(String name) ThisClass_
  static void mthdc_load(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    if (result_pp) // Do nothing if result not desired
      {
      // Determine class of object to load
      SkClass * class_p = &((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
      UClass * uclass_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(class_p);
      SK_ASSERTX(uclass_p, a_cstr_format("Cannot load entity '%s' as the UE4 equivalent of class type '%s' is not known to SkookumScript.", scope_p->get_arg<SkString>(SkArg_1).as_cstr(), class_p->get_name_cstr_dbg()));

      // Load object
      UObject * obj_p = nullptr;
      if (uclass_p)
        {
        obj_p = StaticLoadObject(uclass_p, SkUEClassBindingHelper::get_world(), *AStringToFString(scope_p->get_arg<SkString>(SkArg_1)));
        }

      *result_pp = obj_p ? SkUEEntity::new_instance(obj_p, uclass_p, class_p) : SkBrain::ms_nil_p;
      }
    }
//---------------------------------------------------------------------------------------
// Resolve the raw data info of each raw data member of the given class
void SkUEClassBindingHelper::resolve_raw_data(SkClass * class_p, UStruct * ue_struct_or_class_p)
  {
  // This loop assumes that the data members of the Sk class were created from this very UE4 class
  // I.e. that therefore, except for unsupported properties, they must be in the same order
  // So all we should have to do is loop forward and skip the occasional non-exported UE4 property
  UProperty * ue_var_p = nullptr;
  ASymbol ue_var_name;
  TFieldIterator<UProperty> property_it(ue_struct_or_class_p, EFieldIteratorFlags::ExcludeSuper);
  tSkTypedNameRawArray & raw_data = class_p->get_instance_data_raw_for_resolving();
  for (auto var_p : raw_data)
    {
    // Skip variable if already resolved
    if (var_p->m_raw_data_info != SkRawDataInfo_Invalid)
      { 
      continue;
      }

    // Try to find it in the UE4 reflection data
    while (property_it)
      {
      ue_var_p = *property_it;
      ue_var_name = ASymbol::create_existing(FStringToAString(FSkookumScriptGeneratorBase::skookify_var_name(ue_var_p->GetName(), ue_var_p->IsA(UBoolProperty::StaticClass()), true)));
      ++property_it;
      if (var_p->get_name() == ue_var_name) break;
      }

    // Store raw data info in the raw data member object
    if (var_p->get_name() == ue_var_name)
      {
      var_p->m_raw_data_info = compute_raw_data_info(ue_var_p);
      }
    else
      {
      // Oops didn't find matching variable
      // This is probably due to an unsaved blueprint variable change in the UE4 editor during the previous session
      // If this is the case, a recompile would have been triggered when this class was loaded by get_ue_class_from_sk_class()
      // Which means binaries would be recompiled and reloaded once more, fixing this issue
      // So make sure this assumption is true
      SK_ASSERTX(FModuleManager::Get().GetModulePtr<ISkookumScriptRuntime>("SkookumScriptRuntime")->is_freshen_binaries_pending(), a_str_format("Sk Variable '%s.%s' not found in UE4 reflection data.", class_p->get_name_cstr_dbg(), var_p->get_name_cstr()));
      }
    }
  }
  //---------------------------------------------------------------------------------------
  // Entity@load(String name) ThisClass_
  static void mthdc_load(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    // Load it regardless if a result is desired

    // Determine class of object to load
    SkClass * sk_class_p = ((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
    UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(sk_class_p);
    SK_ASSERTX(ue_class_p, a_cstr_format("Cannot load entity '%s' as the UE4 equivalent of class type '%s' is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", scope_p->get_arg<SkString>(SkArg_1).as_cstr(), sk_class_p->get_name_cstr_dbg()));

    // Load object
    UObject * obj_p = nullptr;
    if (ue_class_p)
      {
      obj_p = StaticLoadObject(ue_class_p, SkUEClassBindingHelper::get_world(), *AStringToFString(scope_p->get_arg<SkString>(SkArg_1)));
      }

    // Set result if desired
    if (result_pp)
      {
      *result_pp = obj_p ? SkUEEntity::new_instance(obj_p, ue_class_p, sk_class_p) : SkBrain::ms_nil_p;
      }
    }