//--------------------------------------------------------------------------------------- // 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; } }