diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index 491341d6..cd384525 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -33,7 +33,10 @@ namespace scripting utils::hook::detour scr_set_thread_position_hook; utils::hook::detour process_script_hook; + utils::hook::detour sl_get_canonical_string_hook; + std::string current_file; + unsigned int current_file_id{}; void vm_notify_stub(const unsigned int notify_list_owner_id, const game::scr_string_t string_value, game::VariableValue* top) @@ -82,24 +85,48 @@ namespace scripting void process_script_stub(const char* filename) { - current_file = filename; - const auto file_id = atoi(filename); if (file_id) { - current_file = scripting::find_token(file_id); + current_file_id = file_id; + } + else + { + current_file_id = 0; + current_file = filename; } process_script_hook.invoke(filename); } - void scr_set_thread_position_stub(unsigned int threadName, const char* codePos) + void add_function(const std::string& file, unsigned int id, const char* pos) { - const auto function_name = scripting::find_token(threadName); - script_function_table[current_file][function_name] = codePos; - scr_set_thread_position_hook.invoke(threadName, codePos); + const auto function_names = scripting::find_token(id); + for (const auto& name : function_names) + { + script_function_table[file][name] = pos; + } } + void scr_set_thread_position_stub(unsigned int thread_name, const char* code_pos) + { + if (current_file_id) + { + const auto names = scripting::find_token(current_file_id); + for (const auto& name : names) + { + add_function(name, thread_name, code_pos); + } + } + else + { + add_function(current_file, thread_name, code_pos); + } + + scr_set_thread_position_hook.invoke(thread_name, code_pos); + } + + char sv_check_load_level_stub(void* save_game) { const auto result = sv_check_load_level_hook.invoke(save_game); @@ -109,6 +136,13 @@ namespace scripting } return result; } + + unsigned int sl_get_canonical_string_stub(const char* str) + { + const auto result = sl_get_canonical_string_hook.invoke(str); + scripting::token_map[str] = result; + return result; + } } class component final : public component_interface @@ -125,6 +159,7 @@ namespace scripting scr_add_class_field_hook.create(0x1405C2C30, scr_add_class_field_stub); scr_set_thread_position_hook.create(0x1405BC7E0, scr_set_thread_position_stub); process_script_hook.create(0x1405C6160, process_script_stub); + sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, sl_get_canonical_string_stub); scheduler::loop([]() { diff --git a/src/client/game/scripting/execution.cpp b/src/client/game/scripting/execution.cpp index c944541f..969efc02 100644 --- a/src/client/game/scripting/execution.cpp +++ b/src/client/game/scripting/execution.cpp @@ -145,47 +145,6 @@ namespace scripting return exec_ent_thread(entity, pos, arguments); } - static std::unordered_map> custom_fields; - - script_value get_custom_field(const entity& entity, const std::string& field) - { - auto& fields = custom_fields[entity.get_entity_id()]; - const auto _field = fields.find(field); - if (_field != fields.end()) - { - return _field->second; - } - return {}; - } - - void set_custom_field(const entity& entity, const std::string& field, const script_value& value) - { - const auto id = entity.get_entity_id(); - - if (custom_fields[id].find(field) != custom_fields[id].end()) - { - custom_fields[id][field] = value; - return; - } - - custom_fields[id].insert(std::make_pair(field, value)); - } - - void clear_entity_fields(const entity& entity) - { - const auto id = entity.get_entity_id(); - - if (custom_fields.find(id) != custom_fields.end()) - { - custom_fields[id].clear(); - } - } - - void clear_custom_fields() - { - custom_fields.clear(); - } - void set_entity_field(const entity& entity, const std::string& field, const script_value& value) { const auto entref = entity.get_entity_reference(); @@ -206,7 +165,7 @@ namespace scripting } else { - set_custom_field(entity, field, value); + set_object_variable(entity.get_entity_id(), field, value); } } @@ -233,7 +192,7 @@ namespace scripting return value; } - return get_custom_field(entity, field); + return get_object_variable(entity.get_entity_id(), field); } unsigned int make_array() @@ -246,4 +205,47 @@ namespace scripting return index; } + + void set_object_variable(const unsigned int parent_id, const unsigned int id, const script_value& value) + { + const auto offset = 0xA000 * (parent_id & 3); + const auto variable_id = game::GetVariable(parent_id, id); + const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + offset]; + const auto& raw_value = value.get_raw(); + + game::AddRefToValue(raw_value.type, raw_value.u); + game::RemoveRefToValue(variable->type, variable->u.u); + + variable->type = static_cast(raw_value.type); + variable->u.u = raw_value.u; + } + + void set_object_variable(const unsigned int parent_id, const std::string& name, const script_value& value) + { + const auto id = scripting::find_token_id(name); + set_object_variable(parent_id, id, value); + } + + script_value get_object_variable(const unsigned int parent_id, const unsigned int id) + { + const auto offset = 0xA000 * (parent_id & 3); + const auto variable_id = game::FindVariable(parent_id, id); + if (!variable_id) + { + return {}; + } + + const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + offset]; + game::VariableValue value{}; + value.type = static_cast(variable->type); + value.u = variable->u.u; + + return value; + } + + script_value get_object_variable(const unsigned int parent_id, const std::string& name) + { + const auto id = scripting::find_token_id(name); + return get_object_variable(parent_id, id); + } } diff --git a/src/client/game/scripting/execution.hpp b/src/client/game/scripting/execution.hpp index 80b3945f..e63019ff 100644 --- a/src/client/game/scripting/execution.hpp +++ b/src/client/game/scripting/execution.hpp @@ -30,13 +30,16 @@ namespace scripting script_value call_script_function(const entity& entity, const std::string& filename, const std::string& function, const std::vector& arguments); - void clear_entity_fields(const entity& entity); - void clear_custom_fields(); - void set_entity_field(const entity& entity, const std::string& field, const script_value& value); script_value get_entity_field(const entity& entity, const std::string& field); void notify(const entity& entity, const std::string& event, const std::vector& arguments); unsigned int make_array(); + + script_value get_object_variable(const unsigned int parent_id, const unsigned int id); + script_value get_object_variable(const unsigned int parent_id, const std::string& name); + + void set_object_variable(const unsigned int parent_id, const std::string& name, const script_value& value); + void set_object_variable(const unsigned int parent_id, const unsigned int id, const script_value& value); } diff --git a/src/client/game/scripting/functions.cpp b/src/client/game/scripting/functions.cpp index 0a3da8b3..533d34bb 100644 --- a/src/client/game/scripting/functions.cpp +++ b/src/client/game/scripting/functions.cpp @@ -69,19 +69,40 @@ namespace scripting return reinterpret_cast(method_table)[index - 0x8000]; } + + unsigned int parse_token_id(const std::string& name) + { + if (name.starts_with("_ID")) + { + return static_cast(std::strtol(name.substr(3).data(), nullptr, 10)); + } + + if (name.starts_with("_id_")) + { + return static_cast(std::strtol(name.substr(4).data(), nullptr, 16)); + } + + return 0; + } } - std::string find_token(unsigned int id) + std::vector find_token(unsigned int id) { + std::vector results; + + results.push_back(utils::string::va("_id_%X", id)); + results.push_back(utils::string::va("_ID%i", id)); + for (const auto& token : token_map) { if (token.second == id) { - return token.first; + results.push_back(token.first); + break; } } - return utils::string::va("_ID%i", id); + return results; } unsigned int find_token_id(const std::string& name) @@ -93,7 +114,13 @@ namespace scripting return result->second; } - return 0; + const auto parsed_id = parse_token_id(name); + if (parsed_id) + { + return parsed_id; + } + + return game::SL_GetCanonicalString(name.data()); } script_function find_function(const std::string& name, const bool prefer_global) diff --git a/src/client/game/scripting/functions.hpp b/src/client/game/scripting/functions.hpp index b788b500..b6def198 100644 --- a/src/client/game/scripting/functions.hpp +++ b/src/client/game/scripting/functions.hpp @@ -10,7 +10,8 @@ namespace scripting using script_function = void(*)(game::scr_entref_t); - std::string find_token(unsigned int id); + std::vector find_token(unsigned int id); unsigned int find_token_id(const std::string& name); + script_function find_function(const std::string& name, const bool prefer_global); } diff --git a/src/client/game/scripting/lua/engine.cpp b/src/client/game/scripting/lua/engine.cpp index d9f0ae0e..d7fb2241 100644 --- a/src/client/game/scripting/lua/engine.cpp +++ b/src/client/game/scripting/lua/engine.cpp @@ -45,7 +45,6 @@ namespace scripting::lua::engine void start() { - clear_custom_fields(); get_scripts().clear(); load_generic_script(); diff --git a/src/client/game/scripting/lua/value_conversion.cpp b/src/client/game/scripting/lua/value_conversion.cpp index 563ece58..c7acc791 100644 --- a/src/client/game/scripting/lua/value_conversion.cpp +++ b/src/client/game/scripting/lua/value_conversion.cpp @@ -176,62 +176,33 @@ namespace scripting::lua auto table = sol::table::create(state); auto metatable = sol::table::create(state); - const auto offset = 0xA000 * (parent_id & 3); - - metatable[sol::meta_function::new_index] = [offset, parent_id](const sol::table t, const sol::this_state s, + metatable[sol::meta_function::new_index] = [parent_id](const sol::table t, const sol::this_state s, const sol::lua_value& field, const sol::lua_value& value) { - const auto id = field.is() - ? scripting::find_token_id(field.as()) - : field.as(); - - if (!id) + const auto new_variable = convert({s, value}); + if (field.is()) { - return; + scripting::set_object_variable(parent_id, field.as(), new_variable); } - - const auto variable_id = game::FindVariable(parent_id, id); - if (!variable_id) + else if (field.is()) { - return; + scripting::set_object_variable(parent_id, field.as(), new_variable); } - - const auto variable = &game::scr_VarGlob->childVariableValue[variable_id + offset]; - - const auto new_variable = convert({s, value}).get_raw(); - - game::AddRefToValue(new_variable.type, new_variable.u); - game::RemoveRefToValue(variable->type, variable->u.u); - - variable->type = (char)new_variable.type; - variable->u.u = new_variable.u; }; - metatable[sol::meta_function::index] = [offset, parent_id](const sol::table t, const sol::this_state s, + metatable[sol::meta_function::index] = [parent_id](const sol::table t, const sol::this_state s, const sol::lua_value& field) { - const auto id = field.is() - ? scripting::find_token_id(field.as()) - : field.as(); - - if (!id) + if (field.is()) { - return sol::lua_value{s, sol::lua_nil}; + return convert(s, scripting::get_object_variable(parent_id, field.as())); + } + else if (field.is()) + { + return convert(s, scripting::get_object_variable(parent_id, field.as())); } - const auto variable_id = game::FindVariable(parent_id, id); - if (!variable_id) - { - return sol::lua_value{s, sol::lua_nil}; - } - - const auto variable = game::scr_VarGlob->childVariableValue[variable_id + offset]; - - game::VariableValue result{}; - result.u = variable.u.u; - result.type = (game::scriptType_e)variable.type; - - return convert(s, result); + return sol::lua_value{s, sol::lua_nil}; }; table[sol::metatable_key] = metatable; diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 959b956b..426db63d 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -136,6 +136,7 @@ namespace game WEAK symbol SL_ConvertToString{0x1405BFBB0}; WEAK symbol SL_GetString{0x1405C0170}; + WEAK symbol SL_GetCanonicalString{0x1405BC970}; WEAK symbol SV_Loaded{0x1406B3860};