diff --git a/src/component/gsc.cpp b/src/component/gsc.cpp index 203f056..e64ff79 100644 --- a/src/component/gsc.cpp +++ b/src/component/gsc.cpp @@ -102,6 +102,18 @@ namespace gsc auto function_map_start = 0x200; auto method_map_start = 0x8400; + auto token_map_start = 0x8000; + auto field_offset_start = 0xA000; + + struct field + { + std::string name; + std::function getter; + std::function setter; + }; + + std::vector> post_load_callbacks; + std::unordered_map> custom_fields; void call_function(unsigned int id) { @@ -199,6 +211,65 @@ namespace gsc retn } } + + utils::hook::detour scr_get_object_field_hook; + void scr_get_object_field_stub(unsigned int classnum, int entnum, unsigned int offset) + { + if (custom_fields[classnum].find(offset) == custom_fields[classnum].end()) + { + return scr_get_object_field_hook.invoke(classnum, entnum, offset); + } + + const auto field = custom_fields[classnum][offset]; + + try + { + const auto result = field.getter(entnum); + return_value(result); + } + catch (const std::exception& e) + { + printf("************** Script execution error **************\n"); + printf("Error getting field %s\n", field.name.data()); + printf("%s\n", e.what()); + printf("****************************************************\n"); + } + } + + utils::hook::detour scr_set_object_field_hook; + void scr_set_object_field_stub(unsigned int classnum, int entnum, unsigned int offset) + { + if (custom_fields[classnum].find(offset) == custom_fields[classnum].end()) + { + return scr_set_object_field_hook.invoke(classnum, entnum, offset); + } + + const auto args = get_arguments(); + const auto field = custom_fields[classnum][offset]; + + try + { + field.setter(entnum, args[0]); + } + catch (const std::exception& e) + { + printf("************** Script execution error **************\n"); + printf("Error setting field %s\n", field.name.data()); + printf("%s\n", e.what()); + printf("****************************************************\n"); + } + } + + utils::hook::detour scr_post_load_scripts_hook; + void scr_post_load_scripts_stub() + { + for (const auto& callback : post_load_callbacks) + { + callback(); + } + + return scr_post_load_scripts_hook.invoke(); + } } namespace function @@ -223,11 +294,61 @@ namespace gsc } } + namespace field + { + void add(classid classnum, const std::string& name, + const std::function& getter, + const std::function& setter) + { + const auto token_id = token_map_start++; + const auto offset = field_offset_start++; + + custom_fields[classnum][offset] = {name, getter, setter}; + (*game::plutonium::token_map_rev)[name] = token_id; + + post_load_callbacks.push_back([classnum, name, token_id, offset]() + { + const auto name_str = game::SL_GetString(name.data(), 0); + game::Scr_AddClassField(classnum, name_str, token_id, offset); + }); + } + } + class component final : public component_interface { public: void post_unpack() override { + scr_get_object_field_hook.create(0x52BDB0, scr_get_object_field_stub); + scr_set_object_field_hook.create(0x52BCC0, scr_set_object_field_stub); + scr_post_load_scripts_hook.create(0x628B50, scr_post_load_scripts_stub); + + gsc::field::add(classid::entity, "flags", + [](unsigned int entnum) -> scripting::script_value + { + const auto entity = &game::g_entities[entnum]; + return entity->flags; + }, + [](unsigned int entnum, const scripting::script_value& value) + { + const auto entity = &game::g_entities[entnum]; + entity->flags = value.as(); + } + ); + + gsc::field::add(classid::entity, "clientflags", + [](unsigned int entnum) -> scripting::script_value + { + const auto entity = &game::g_entities[entnum]; + return entity->client->flags; + }, + [](unsigned int entnum, const scripting::script_value& value) + { + const auto entity = &game::g_entities[entnum]; + entity->client->flags = value.as(); + } + ); + function::add("executecommand", [](const function_args& args) -> scripting::script_value { game::Cbuf_AddText(0, args[0].as()); diff --git a/src/component/gsc.hpp b/src/component/gsc.hpp index 8b9c687..fcdafeb 100644 --- a/src/component/gsc.hpp +++ b/src/component/gsc.hpp @@ -2,6 +2,15 @@ namespace gsc { + enum classid + { + entity, + hudelem, + pathnode, + node, + count + }; + class function_args { public: @@ -34,4 +43,11 @@ namespace gsc { void add(const std::string& name, const script_method& func); } + + namespace field + { + void add(classid classnum, const std::string& name, + const std::function& getter, + const std::function& setter); + } } \ No newline at end of file diff --git a/src/component/json.cpp b/src/component/json.cpp index 01ba058..e6fba65 100644 --- a/src/component/json.cpp +++ b/src/component/json.cpp @@ -28,18 +28,23 @@ namespace json const auto keys = array.get_keys(); for (auto i = 0; i < keys.size(); i++) { + const auto is_int = keys[i].is(); + const auto is_string = keys[i].is(); + if (string_indexed == -1) { - string_indexed = keys[i].is_string; + string_indexed = is_string; } - if (!string_indexed && keys[i].is_integer) + if (!string_indexed && is_int) { - obj[keys[i].index] = gsc_to_json(array[keys[i].index]); + const auto index = keys[i].as(); + obj[index] = gsc_to_json(array[index]); } - else if (string_indexed && keys[i].is_string) + else if (string_indexed && is_string) { - obj.emplace(keys[i].key, gsc_to_json(array[keys[i].key])); + const auto key = keys[i].as(); + obj.emplace(key, gsc_to_json(array[key])); } } diff --git a/src/game/scripting/array.cpp b/src/game/scripting/array.cpp index df3e22c..8ef8e75 100644 --- a/src/game/scripting/array.cpp +++ b/src/game/scripting/array.cpp @@ -129,9 +129,9 @@ namespace scripting } } - std::vector array::get_keys() const + std::vector array::get_keys() const { - std::vector result; + std::vector result; const auto offset = 0xC800 * (this->id_ & 1); auto current = game::scr_VarGlob->objectVariableChildren[this->id_].firstChild; @@ -149,16 +149,14 @@ namespace scripting const auto string_value = (unsigned int)((unsigned __int8)var.name_lo + (var.k.keys.name_hi << 8)); const auto* str = game::SL_ConvertToString(string_value); - array_key key; + script_value key; if (string_value < 0x40000 && str) { - key.is_string = true; - key.key = str; + key = str; } else { - key.is_integer = true; - key.index = (string_value - 0x800000) & 0xFFFFFF; + key = (string_value - 0x800000) & 0xFFFFFF; } result.push_back(key); @@ -206,16 +204,18 @@ namespace scripting return value; } - script_value array::get(const array_key& key) const + script_value array::get(const script_value& key) const { - if (key.is_integer) + if (key.is()) { - return this->get(key.index); + return this->get(key.as()); } else { - return this->get(key.key); + return this->get(key.as()); } + + return {}; } script_value array::get(const std::string& key) const @@ -253,15 +253,15 @@ namespace scripting return variable; } - void array::set(const array_key& key, const script_value& value) const + void array::set(const script_value& key, const script_value& value) const { - if (key.is_integer) + if (key.is()) { - this->set(key.index, value); + this->set(key.as(), value); } else { - this->set(key.key, value); + this->set(key.as(), value); } } diff --git a/src/game/scripting/array.hpp b/src/game/scripting/array.hpp index cea2335..c31e4cf 100644 --- a/src/game/scripting/array.hpp +++ b/src/game/scripting/array.hpp @@ -3,14 +3,6 @@ namespace scripting { - struct array_key - { - bool is_string = false; - bool is_integer = false; - unsigned int index{}; - std::string key{}; - }; - class array_value : public script_value { public: @@ -38,7 +30,7 @@ namespace scripting array& operator=(const array& other); array& operator=(array&& other) noexcept; - std::vector get_keys() const; + std::vector get_keys() const; unsigned int size() const; unsigned int push(script_value) const; @@ -46,11 +38,11 @@ namespace scripting void erase(const std::string&) const; script_value pop() const; - script_value get(const array_key&) const; + script_value get(const script_value&) const; script_value get(const std::string&) const; script_value get(const unsigned int) const; - void set(const array_key&, const script_value&) const; + void set(const script_value&, const script_value&) const; void set(const std::string&, const script_value&) const; void set(const unsigned int, const script_value&) const; @@ -71,15 +63,17 @@ namespace scripting return {this->id_, this->get_value_id(key)}; } - array_value operator[](const array_key& key) const + template + array_value operator[](const script_value& key) const { - if (key.is_integer) + if (key.is()) { - return {this->id_, this->get_value_id(key.index)}; + return { this->id_, this->get_value_id(key.as()) }; } - else + + if (key.is()) { - return {this->id_, this->get_value_id(key.key)}; + return { this->id_, this->get_value_id(key.as()) }; } } diff --git a/src/game/structs.hpp b/src/game/structs.hpp index 612b14a..a4c5e14 100644 --- a/src/game/structs.hpp +++ b/src/game/structs.hpp @@ -314,7 +314,7 @@ namespace game struct gclient_s { playerState_s ps; - char __pad0[0x2ED]; + char __pad0[0x2CC]; int flags; }; diff --git a/src/game/symbols.hpp b/src/game/symbols.hpp index 71768e1..2842f11 100644 --- a/src/game/symbols.hpp +++ b/src/game/symbols.hpp @@ -49,8 +49,10 @@ namespace game WEAK symbol Scr_GetSelf{0x5655E0}; WEAK symbol Scr_MakeArray{0x56ADE0}; WEAK symbol Scr_AddArrayStringIndexed{0x56AE70}; + WEAK symbol Scr_AddClassField{0x567CD0}; WEAK symbol SL_GetString{0x5649E0}; + WEAK symbol SL_GetCanonicalString{0x5619A0}; WEAK symbol SL_ConvertToString{0x564270}; WEAK symbol SV_GameSendServerCommand{0x573220}; @@ -81,5 +83,6 @@ namespace game { WEAK symbol> function_map_rev{0x20691228}; WEAK symbol> method_map_rev{0x20691248}; + WEAK symbol> token_map_rev{0x20691288}; } } \ No newline at end of file