//--------------------------------------------------------------------------------------- // Gets memory representing binary for class hierarchy and associated info. // // #See Also: load_compiled_scripts(), SkCompiler::get_binary_class_group() // #Modifiers: virtual - overridden from SkookumRuntimeBase // #Author(s): Conan Reis SkBinaryHandle * SkUERuntime::get_binary_symbol_table() { FString sym_file = get_compiled_path() / TEXT("classes.sk-sym"); A_DPRINT(" Loading compiled binary symbol file '%ls'...\n", *sym_file); SkBinaryHandleUE * handle_p = SkBinaryHandleUE::create(*sym_file); // Ensure symbol table binary exists if (!handle_p) { A_DPRINT(" ...it does not exist!\n\n", *sym_file); } return handle_p; }
//--------------------------------------------------------------------------------------- // Gets memory representing binary for class hierarchy and associated info. // // #See Also: load_compiled_scripts(), SkCompiler::get_binary_class_group() // #Modifiers: virtual - overridden from SkookumRuntimeBase // #Author(s): Conan Reis SkBinaryHandle * SkUERuntime::get_binary_hierarchy() { FString compiled_file = get_compiled_path() / TEXT("classes.sk-bin"); A_DPRINT(" Loading compiled binary file '%ls'...\n", *compiled_file); return SkBinaryHandleUE::create(*compiled_file); }
//--------------------------------------------------------------------------------------- // Setups the auto-parse temporary symbol table. Symbol creation calls will put shared copies of // new symbols into the auto-parse symbol table. The auto-parse symbol table with then be used to // remove these temporary symbols once the auto-parse terminates. // // Author(s) John Stenersen void ASymbolTable::track_auto_parse_init() { if (ms_auto_parse_syms_p) { A_DPRINT(A_SOURCE_STR "ms_auto_parse_syms_p is not null. Forgotten call to track_auto_parse_term()?\n"); track_auto_parse_term(); } ms_auto_parse_syms_p = this; }
//--------------------------------------------------------------------------------------- // Override to add bindings to any custom C++ routines (methods & coroutines). // // #See Also SkBrain::register_bind_atomics_func() // #Modifiers virtual // #Author(s) Conan Reis void SkUERuntime::on_bind_routines() { A_DPRINT(A_SOURCE_STR "\nBind routines for SkUERuntime.\n"); #if WITH_EDITORONLY_DATA SkUEClassBindingHelper::reset_dynamic_class_mappings(); // Start over fresh #endif SkUEBindings::register_all_bindings(); m_blueprint_interface.reexpose_all(); // Hook up Blueprint functions and events for static classes }
//--------------------------------------------------------------------------------------- void FSkookumScriptRuntime::on_world_cleanup(UWorld * world_p, bool session_ended_b, bool cleanup_resources_b) { //A_DPRINT("on_world_cleanup: %S %p\n", *world_p->GetName(), world_p); if (world_p->IsGameWorld()) { // Keep track of how many game worlds we got --m_num_game_worlds; // Set world pointer to null if it was pointing to us if (m_game_world_p == world_p) { m_game_world_p->OnTickDispatch().Remove(m_game_tick_handle); m_game_world_p = nullptr; SkUEClassBindingHelper::set_world(nullptr); } // Restart SkookumScript if initialized if (m_num_game_worlds == 0 && is_skookum_initialized()) { // Simple shutdown //SkookumScript::get_world()->clear_coroutines(); A_DPRINT( "SkookumScript resetting session...\n" " cleaning up...\n"); SkookumScript::deinitialize_gameplay(); SkookumScript::deinitialize_sim(); SkookumScript::initialize_sim(); A_DPRINT(" ...done!\n\n"); } } else if (world_p->WorldType == EWorldType::Editor) { // Set world pointer to null if it was pointing to us if (m_editor_world_p == world_p) { m_editor_world_p->OnTickDispatch().Remove(m_editor_tick_handle); m_editor_world_p = nullptr; } } }
//--------------------------------------------------------------------------------------- // This code will execute after your module is loaded into memory (but after global // variables are initialized, of course.) void FSkookumScriptRuntime::StartupModule() { #if WITH_EDITORONLY_DATA // In editor builds, don't activate SkookumScript if there's no project (project wizard mode) if (!FApp::GetGameName() || !FApp::GetGameName()[0] || FPlatformString::Strcmp(FApp::GetGameName(), TEXT("None")) == 0) { m_is_skookum_disabled = true; return; } #else // In cooked builds, stay inert when there's no compiled binaries if (!m_runtime.is_binary_hierarchy_existing()) { m_is_skookum_disabled = true; return; } #endif A_DPRINT("Starting up SkookumScript plug-in modules\n"); // Note that FWorldDelegates::OnPostWorldCreation has world_p->WorldType set to None // Note that FWorldDelegates::OnPreWorldFinishDestroy has world_p->GetName() set to "None" m_on_world_init_pre_handle = FWorldDelegates::OnPreWorldInitialization.AddRaw(this, &FSkookumScriptRuntime::on_world_init_pre); m_on_world_init_post_handle = FWorldDelegates::OnPostWorldInitialization.AddRaw(this, &FSkookumScriptRuntime::on_world_init_post); m_on_world_cleanup_handle = FWorldDelegates::OnWorldCleanup.AddRaw(this, &FSkookumScriptRuntime::on_world_cleanup); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Start up SkookumScript // Originally, the compiled binaries were loaded with a delay when in UE4Editor to provide the user with a smoother startup sequence // However this caused issues with the proper initialization of Skookum Blueprint nodes // So to avoid glitches, SkookumScript is always initialized right away right here //#if WITH_EDITORONLY_DATA // if (!GIsEditor) //#endif { // Initialize right away ensure_runtime_initialized(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Send off connect request to IDE // Come back later to check on it #ifdef SKOOKUM_REMOTE_UNREAL if (!IsRunningCommandlet()) { m_remote_client.set_mode(SkLocale_runtime); } #endif }
//--------------------------------------------------------------------------------------- // This function may be called during shutdown to clean up your module. For modules that // support dynamic reloading, we call this function before unloading the module. void FSkookumScriptRuntime::ShutdownModule() { A_DPRINT(A_SOURCE_STR " Shutting down SkookumScript plug-in modules\n"); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Clean up SkookumScript m_runtime.shutdown(); #ifdef SKOOKUM_REMOTE_UNREAL // Remote communication to and from SkookumScript IDE m_remote_client.disconnect(); #endif // Clear out our registered delegates FWorldDelegates::OnPreWorldInitialization.Remove(m_on_world_init_pre_handle); FWorldDelegates::OnPostWorldInitialization.Remove(m_on_world_init_post_handle); FWorldDelegates::OnWorldCleanup.Remove(m_on_world_cleanup_handle); }
//--------------------------------------------------------------------------------------- // 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; }
//--------------------------------------------------------------------------------------- // Setups the auto-parse temporary symbol table. Symbol creation calls will put shared copies of // new symbols into the auto-parse symbol table. The auto-parse symbol table with then be used to // remove these temporary symbols once the auto-parse terminates. // // Author(s) John Stenersen void ASymbolTable::track_auto_parse_term() { if (!ms_auto_parse_syms_p) { A_DPRINT(A_SOURCE_STR "ms_auto_parse_syms_p is null (terminated) already.?\n"); return; } // Remove any symbols found in the auto-parse symbol table from the main symbol table. uint32_t length = ms_auto_parse_syms_p->get_length(); for (uint32_t i = 0; i < length; i++) { ASymbolRef * sym_ref = ms_auto_parse_syms_p->m_sym_refs.get_at(i); ms_main_p->m_sym_refs.remove(sym_ref->m_uid, AMatch_first_found); //A_DPRINT(A_SOURCE_STR "Removing symbol = %ld\n", sym_ref->m_uid); } ms_auto_parse_syms_p = nullptr; }
//--------------------------------------------------------------------------------------- // Load the Skookum class hierarchy scripts in compiled binary form. // // #Params // ensure_atomics: // If set makes sure all atomic (C++) scripts that were expecting a C++ function to be // bound/hooked up to them do actually have a binding. // ignore_classes_pp: // array of classes to ignore when ensure_atomics is set. // This allows some classes with optional or delayed bindings to be skipped such as // bindings to a in-game world editor. // ignore_count: number of class pointers in ignore_classes_pp // // #Returns // true if compiled scrips successfully loaded, false if not // // #See: load_compiled_class_group(), SkCompiler::compiled_load() // #Modifiers: static // #Author(s): Conan Reis bool SkUERuntime::load_compiled_scripts( bool ensure_atomics, // = true SkClass ** ignore_classes_pp, // = nullptr uint32_t ignore_count // = 0u ) { A_DPRINT("\nSkookumScript loading previously parsed compiled binary...\n"); if (load_compiled_hierarchy() != SkLoadStatus_ok) { return false; } A_DPRINT(" ...done!\n\n"); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Bind atomics A_DPRINT("SkookumScript binding with C++ routines...\n"); // Registers/connects Generic SkookumScript atomic classes, stimuli, coroutines, etc. // with the compiled binary that was just loaded. SkookumScript::initialize_post_load(); #if (SKOOKUM & SK_DEBUG) // Ensure atomic (C++) methods/coroutines are properly bound to their C++ equivalents if (ensure_atomics) { SkBrain::ensure_atomics_registered(ignore_classes_pp, ignore_count); } #endif A_DPRINT(" ...done!\n\n"); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Enable SkookumScript evaluation SkookumScript::enable_flag(SkookumScript::Flag_evaluate); A_DPRINT("SkookumScript initializing session...\n"); SkookumScript::initialize_session(); A_DPRINT(" ...done!\n\n"); return true; }
//--------------------------------------------------------------------------------------- // This code will execute after your module is loaded into memory (but after global // variables are initialized, of course.) void FSkookumScriptRuntime::StartupModule() { A_DPRINT(A_SOURCE_STR " Starting up SkookumScript plug-in modules\n"); // Note that FWorldDelegates::OnPostWorldCreation has world_p->WorldType set to None // Note that FWorldDelegates::OnPreWorldFinishDestroy has world_p->GetName() set to "None" m_on_world_init_pre_handle = FWorldDelegates::OnPreWorldInitialization.AddRaw(this, &FSkookumScriptRuntime::on_world_init_pre); m_on_world_init_post_handle = FWorldDelegates::OnPostWorldInitialization.AddRaw(this, &FSkookumScriptRuntime::on_world_init_post); m_on_world_cleanup_handle = FWorldDelegates::OnWorldCleanup.AddRaw(this, &FSkookumScriptRuntime::on_world_cleanup); // Hook up Unreal memory allocator AMemory::override_functions(&Agog::malloc_func, &Agog::free_func, &Agog::req_byte_size_func); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Start up SkookumScript #if !WITH_EDITOR // If no editor, initialize right away // otherwise wait until map is loaded m_runtime.startup(); #ifndef SKOOKUM_REMOTE_UNREAL bool success_b = m_runtime.load_compiled_scripts(); SK_ASSERTX(success_b, AErrMsg("Unable to load SkookumScript compiled binaries!", AErrLevel_notify)); #endif #endif //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Send off connect request to IDE // Come back later to check on it #ifdef SKOOKUM_REMOTE_UNREAL if (!IsRunningCommandlet()) { m_remote_client.set_mode(SkLocale_runtime); } #endif }
//--------------------------------------------------------------------------------------- // Converts any occurrences of symbol ids in the form |#12345678#| to their string // equivalents. // // Returns: // `true` if completely converted and `false` if there were some ids that the string // equivalent could not be found for. // // Params: // str_p: pointer to string to convert // // Author(s): Conan Reis bool ASymbolTable::translate_ids(AString * str_p) const { uint32_t length = str_p->get_length(); const uint32_t ASymbol_id_str_length = 12u; if (length < ASymbol_id_str_length) { return true; } bool sym_replaced; bool translated = true; uint32_t sym_id; uint32_t str_length; uint32_t find_idx; uint32_t end_idx; uint32_t start_idx = 0u; uint32_t max_idx = length - ASymbol_id_str_length; AString sym_str; const char * cstr_p = str_p->as_cstr(); // If found special |#12345678#| pattern while ((start_idx <= max_idx) && str_p->find('|', 1u, &find_idx, start_idx) && (find_idx <= max_idx) && (cstr_p[find_idx + 1u] == '#') && (cstr_p[find_idx + ASymbol_id_str_length - 2u] == '#') && (cstr_p[find_idx + ASymbol_id_str_length - 1u] == '|')) { sym_replaced = false; // $Revisit - CReis Ensure that ids with leading zeros 00123 are converted properly sym_id = str_p->as_uint(find_idx + 1u, &end_idx, 16u); if (end_idx == find_idx + ASymbol_id_str_length - 1u) { if (sym_id != ASymbol_id_null) { if (translate_known_id(sym_id, &sym_str)) { // Replace symbol id with corresponding string str_p->replace(sym_str, find_idx, ASymbol_id_str_length); str_length = sym_str.get_length(); max_idx = max_idx + str_length - ASymbol_id_str_length; start_idx = find_idx + str_length; sym_replaced = true; } else { // Unable to translate all of supplied string translated = false; } } else { // It is the empty symbol "" - remove symbol id str_p->remove_all(find_idx, ASymbol_id_str_length); max_idx -= ASymbol_id_str_length; start_idx = find_idx; sym_replaced = true; } } else { translated = false; A_DPRINT("Bad symbol id!\n"); } if (!sym_replaced) { start_idx = find_idx + ASymbol_id_str_length; } } // while return translated; }
//--------------------------------------------------------------------------------------- // Called before the module has been unloaded void FSkookumScriptRuntime::PreUnloadCallback() { A_DPRINT(A_SOURCE_STR " SkookumScript - about to unload.\n"); }
//--------------------------------------------------------------------------------------- // Called after the module has been reloaded void FSkookumScriptRuntime::PostLoadCallback() { A_DPRINT(A_SOURCE_STR " SkookumScript - loaded.\n"); }