diff --git a/src/client/component/gsc.cpp b/src/client/component/gsc.cpp index 5226da19..d82e4455 100644 --- a/src/client/component/gsc.cpp +++ b/src/client/component/gsc.cpp @@ -41,7 +41,7 @@ namespace gsc std::unordered_map main_handles; std::unordered_map init_handles; std::unordered_map loaded_scripts; - std::unordered_map functions; + std::unordered_map functions; std::optional gsc_error; char* allocate_buffer(size_t size) @@ -185,21 +185,6 @@ namespace gsc loaded_scripts.clear(); } - void gscr_print_stub() - { - const auto num = game::Scr_GetNumParam(); - std::string buffer{}; - - for (auto i = 0; i < num; i++) - { - const auto str = game::Scr_GetString(i); - buffer.append(str); - buffer.append("\t"); - } - - printf("%s\n", buffer.data()); - } - void load_gametype_script_stub(void* a1, void* a2) { utils::hook::invoke(0x1404E1400, a1, a2); @@ -318,13 +303,11 @@ namespace gsc bool force_error_print = false; void* vm_error_stub(void* a1) { - if (!developer_script->current.enabled || force_error_print) + if (!developer_script->current.enabled && !force_error_print) { return utils::hook::invoke(0x140614670, a1); } - force_error_print = false; - console::warn("*********** script runtime error *************\n"); const auto opcode_id = *reinterpret_cast(0x14BAA93E8); @@ -343,6 +326,9 @@ namespace gsc opcode_id, error_str.data()); } + force_error_print = false; + gsc_error = {}; + print_callstack(); console::warn("**********************************************\n"); return utils::hook::invoke(0x140614670, a1); @@ -359,7 +345,7 @@ namespace gsc utils::hook::invoke(0x140509F20, a1, a2); for (const auto& func : functions) { - game::Scr_RegisterFunction(func.second, 0, func.first); + game::Scr_RegisterFunction(func.first, 0, func.second); } } @@ -378,33 +364,54 @@ namespace gsc auto function_id_start = 0x320; void add_function(const std::string& name, scripting::script_function function) { - const auto id = ++function_id_start; - scripting::function_map[name] = id; - functions[id] = function; - } - - void set_gsc_error(const std::string& error) - { - force_error_print = true; - gsc_error = error; - game::Scr_ErrorInternal(); - } - - void assert_cmd() - { - const auto expr = get_argument(0).as(); - if (!expr) + if (scripting::function_map.find(name) != scripting::function_map.end()) { - set_gsc_error("assert fail"); + const auto id = scripting::function_map[name]; + functions[function] = id; + } + else + { + const auto id = ++function_id_start; + scripting::function_map[name] = id; + functions[function] = id; } } - void assertex_cmd() + void execute_custom_function(scripting::script_function function) { - const auto expr = get_argument(0).as(); - if (!expr) + bool error = false; + + try { - set_gsc_error(get_argument(1).as()); + function({}); + } + catch (const std::exception& e) + { + error = true; + force_error_print = true; + gsc_error = e.what(); + } + + if (error) + { + game::Scr_ErrorInternal(); + } + } + + void vm_call_builtin_stub(scripting::script_function function) + { + bool custom = false; + { + custom = functions.find(function) != functions.end(); + } + + if (!custom) + { + function({}); + } + else + { + execute_custom_function(function); } } } @@ -466,13 +473,6 @@ namespace gsc utils::hook::call(0x1404C8F71, g_load_structs_stub); utils::hook::call(0x1404C8F80, scr_load_level_stub); - // replace builtin print function - utils::hook::jump(0x1404EC640, gscr_print_stub); - - // restore assert - utils::hook::jump(0x1404EC890, assert_cmd); - utils::hook::jump(0x1404EC920, assertex_cmd); - utils::hook::call(0x1405CB94F, vm_error_stub); utils::hook::call(0x1405BC583, unknown_function_stub); @@ -487,31 +487,59 @@ namespace gsc utils::hook::inject(0x1405BCB78 + 3, &func_table); utils::hook::set(0x1405CA678 + 4, RVA(&func_table)); + utils::hook::nop(0x1405CA683, 8); + utils::hook::call(0x1405CA683, vm_call_builtin_stub); + + add_function("print", [](const game::scr_entref_t ref) + { + const auto num = game::Scr_GetNumParam(); + std::string buffer{}; + + for (auto i = 0; i < num; i++) + { + const auto str = game::Scr_GetString(i); + buffer.append(str); + buffer.append("\t"); + } + + printf("%s\n", buffer.data()); + }); + + add_function("assert", [](const game::scr_entref_t ref) + { + const auto expr = get_argument(0).as(); + if (!expr) + { + throw std::runtime_error("assert fail"); + } + }); + + add_function("assertex", [](const game::scr_entref_t ref) + { + const auto expr = get_argument(0).as(); + if (!expr) + { + const auto error = get_argument(1).as(); + throw std::runtime_error(error); + } + }); + add_function("replacefunc", [](const game::scr_entref_t ref) { - try + const auto what = get_argument(0).get_raw(); + const auto with = get_argument(1).get_raw(); + + if (what.type != game::SCRIPT_FUNCTION) { - const auto what = get_argument(0).get_raw(); - const auto with = get_argument(1).get_raw(); - - if (what.type != game::SCRIPT_FUNCTION) - { - set_gsc_error("replaceFunc: parameter 0 must be a function"); - return; - } - - if (with.type != game::SCRIPT_FUNCTION) - { - set_gsc_error("replaceFunc: parameter 1 must be a function"); - return; - } - - notifies::set_gsc_hook(what.u.codePosValue, with.u.codePosValue); + throw std::runtime_error("replaceFunc: parameter 1 must be a function"); } - catch (const std::exception& e) + + if (with.type != game::SCRIPT_FUNCTION) { - set_gsc_error(utils::string::va("replaceFunc: %s", e.what())); + throw std::runtime_error("replaceFunc: parameter 2 must be a function"); } + + notifies::set_gsc_hook(what.u.codePosValue, with.u.codePosValue); }); scripting::on_shutdown([](int free_scripts) diff --git a/src/client/component/notifies.cpp b/src/client/component/notifies.cpp index 6bcc05e3..2834b967 100644 --- a/src/client/component/notifies.cpp +++ b/src/client/component/notifies.cpp @@ -152,7 +152,7 @@ namespace notifies } void scr_entity_damage_stub(game::gentity_s* self, game::gentity_s* inflictor, game::gentity_s* attacker, const float* vDir, const float* vPoint, - int damage, int dflags, const unsigned int hitLoc, const unsigned int weapon, bool isAlternate, unsigned int a11, const int meansOfDeath, unsigned int a13, unsigned int a14) + int damage, int dflags, const unsigned int meansOfDeath, const unsigned int weapon, bool isAlternate, unsigned int a11, const int hitLoc, unsigned int a13, unsigned int a14) { { const std::string _hitLoc = reinterpret_cast(0x140BF4AA0)[hitLoc]; @@ -187,7 +187,8 @@ namespace notifies } } - scr_entity_damage_hook.invoke(self, inflictor, attacker, vDir, vPoint, damage, dflags, hitLoc, weapon, isAlternate, a11, meansOfDeath, a13, a14); + scr_entity_damage_hook.invoke(self,inflictor, attacker, vDir, vPoint, damage, dflags, + meansOfDeath, weapon, isAlternate, a11, hitLoc, a13, a14); } } diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 4bdbd931..b4fa7f93 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -444,25 +444,28 @@ namespace ui_scripting reader_data, chunk_name); } } - + + std::string current_error; int main_handler(game::hks::lua_State* state) { - const auto value = state->m_apistack.base[-1]; - if (value.t != game::hks::TCFUNCTION) - { - return 0; - } - - const auto closure = value.v.cClosure; - if (converted_functions.find(closure) == converted_functions.end()) - { - return 0; - } - - const auto& function = converted_functions[closure]; + bool error = false; try { + const auto value = state->m_apistack.base[-1]; + if (value.t != game::hks::TCFUNCTION) + { + return 0; + } + + const auto closure = value.v.cClosure; + if (converted_functions.find(closure) == converted_functions.end()) + { + return 0; + } + + const auto& function = converted_functions[closure]; + const auto args = get_return_values(); const auto results = function(args); @@ -475,7 +478,13 @@ namespace ui_scripting } catch (const std::exception& e) { - game::hks::hksi_luaL_error(state, e.what()); + current_error = e.what(); + error = true; + } + + if (error) + { + game::hks::hksi_luaL_error(state, current_error.data()); } return 0;