#include #include "context.hpp" #include "error.hpp" #include "value_conversion.hpp" #include "../../scripting/execution.hpp" #include "../script_value.hpp" #include "../execution.hpp" #include "../../../component/ui_scripting.hpp" #include "../../../component/scripting.hpp" #include "../../../component/command.hpp" #include "../../../component/fastfiles.hpp" #include "component/game_console.hpp" #include "component/scheduler.hpp" #include #include #include #include #include #include namespace ui_scripting::lua { std::unordered_map menus; std::vector elements; element ui_element; int mouse[2]; namespace { const auto animation_script = utils::nt::load_resource(LUA_ANIMATION_SCRIPT); const auto json_script = utils::nt::load_resource(LUA_JSON_SCRIPT); scripting::script_value script_convert(const sol::lua_value& value) { if (value.is()) { return {value.as()}; } if (value.is()) { return {value.as()}; } if (value.is()) { return {value.as()}; } if (value.is()) { return {value.as()}; } if (value.is()) { return {value.as()}; } if (value.is()) { return {value.as()}; } if (value.is()) { return {value.as()}; } return {}; } bool valid_dvar_name(const std::string& name) { for (const auto c : name) { if (!isalnum(c) && c != '_') { return false; } } return true; } void setup_io(sol::state& state) { state["io"]["fileexists"] = utils::io::file_exists; state["io"]["writefile"] = utils::io::write_file; state["io"]["filesize"] = utils::io::file_size; state["io"]["createdirectory"] = utils::io::create_directory; state["io"]["directoryexists"] = utils::io::directory_exists; state["io"]["directoryisempty"] = utils::io::directory_is_empty; state["io"]["listfiles"] = utils::io::list_files; state["io"]["copyfolder"] = utils::io::copy_folder; state["io"]["removefile"] = utils::io::remove_file; state["io"]["removedirectory"] = utils::io::remove_directory; state["io"]["readfile"] = static_cast(utils::io::read_file); } void setup_json(sol::state& state) { const auto json = state.safe_script(json_script, &sol::script_pass_on_error); handle_error(json); state["json"] = json; } void setup_vector_type(sol::state& state) { auto vector_type = state.new_usertype("vector", sol::constructors()); vector_type["x"] = sol::property(&scripting::vector::get_x, &scripting::vector::set_x); vector_type["y"] = sol::property(&scripting::vector::get_y, &scripting::vector::set_y); vector_type["z"] = sol::property(&scripting::vector::get_z, &scripting::vector::set_z); vector_type["r"] = sol::property(&scripting::vector::get_x, &scripting::vector::set_x); vector_type["g"] = sol::property(&scripting::vector::get_y, &scripting::vector::set_y); vector_type["b"] = sol::property(&scripting::vector::get_z, &scripting::vector::set_z); vector_type[sol::meta_function::addition] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) { return scripting::vector( a.get_x() + b.get_x(), a.get_y() + b.get_y(), a.get_z() + b.get_z() ); }, [](const scripting::vector& a, const int value) { return scripting::vector( a.get_x() + value, a.get_y() + value, a.get_z() + value ); } ); vector_type[sol::meta_function::subtraction] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) { return scripting::vector( a.get_x() - b.get_x(), a.get_y() - b.get_y(), a.get_z() - b.get_z() ); }, [](const scripting::vector& a, const int value) { return scripting::vector( a.get_x() - value, a.get_y() - value, a.get_z() - value ); } ); vector_type[sol::meta_function::multiplication] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) { return scripting::vector( a.get_x() * b.get_x(), a.get_y() * b.get_y(), a.get_z() * b.get_z() ); }, [](const scripting::vector& a, const int value) { return scripting::vector( a.get_x() * value, a.get_y() * value, a.get_z() * value ); } ); vector_type[sol::meta_function::division] = sol::overload( [](const scripting::vector& a, const scripting::vector& b) { return scripting::vector( a.get_x() / b.get_x(), a.get_y() / b.get_y(), a.get_z() / b.get_z() ); }, [](const scripting::vector& a, const int value) { return scripting::vector( a.get_x() / value, a.get_y() / value, a.get_z() / value ); } ); vector_type[sol::meta_function::equal_to] = [](const scripting::vector& a, const scripting::vector& b) { return a.get_x() == b.get_x() && a.get_y() == b.get_y() && a.get_z() == b.get_z(); }; vector_type[sol::meta_function::length] = [](const scripting::vector& a) { return sqrt((a.get_x() * a.get_x()) + (a.get_y() * a.get_y()) + (a.get_z() * a.get_z())); }; vector_type[sol::meta_function::to_string] = [](const scripting::vector& a) { return utils::string::va("{x: %f, y: %f, z: %f}", a.get_x(), a.get_y(), a.get_z()); }; } void setup_element_type(sol::state& state, event_handler& handler, scheduler& scheduler) { auto element_type = state.new_usertype("element", "new", []() { const auto el = new element(); elements.push_back(el); return el; }); element_type["setvertalign"] = &element::set_vertalign; element_type["sethorzalign"] = &element::set_horzalign; element_type["setrect"] = &element::set_rect; element_type["setfont"] = sol::overload( static_cast(&element::set_font), static_cast(&element::set_font) ); element_type["settext"] = &element::set_text; element_type["setmaterial"] = &element::set_material; element_type["setcolor"] = &element::set_color; element_type["setsecondcolor"] = &element::set_second_color; element_type["setglowcolor"] = &element::set_glow_color; element_type["setbackcolor"] = &element::set_background_color; element_type["setbordercolor"] = &element::set_border_color; element_type["setborderwidth"] = sol::overload( static_cast(&element::set_border_width), static_cast(&element::set_border_width), static_cast(&element::set_border_width), static_cast(&element::set_border_width) ); element_type["settextoffset"] = &element::set_text_offset; element_type["setscale"] = &element::set_scale; element_type["setrotation"] = &element::set_rotation; element_type["setyle"] = &element::set_style; element_type["setslice"] = &element::set_slice; element_type["getrect"] = [](const sol::this_state s, element& element) { auto rect = sol::table::create(s.lua_state()); rect["x"] = element.x; rect["y"] = element.y; rect["w"] = element.w + element.border_width[1] + element.border_width[3]; rect["h"] = element.h + element.border_width[0] + element.border_width[2]; return rect; }; element_type["x"] = sol::property( [](element& element) { return element.x; }, [](element& element, float x) { element.x = x; } ); element_type["y"] = sol::property( [](element& element) { return element.y; }, [](element& element, float y) { element.y = y; } ); element_type["w"] = sol::property( [](element& element) { return element.w; }, [](element& element, float w) { element.w = w; } ); element_type["h"] = sol::property( [](element& element) { return element.h; }, [](element& element, float h) { element.h = h; } ); element_type["scalex"] = sol::property( [](element& element) { return element.x_scale; }, [](element& element, float x_scale) { element.x_scale = x_scale; } ); element_type["scaley"] = sol::property( [](element& element) { return element.y_scale; }, [](element& element, float y_scale) { element.y_scale = y_scale; } ); element_type["rotation"] = sol::property( [](element& element) { return element.rotation; }, [](element& element, float rotation) { element.rotation = rotation; } ); element_type["style"] = sol::property( [](element& element) { return element.style; }, [](element& element, int style) { element.style = style; } ); element_type["color"] = sol::property( [](element& element, const sol::this_state s) { auto color = sol::table::create(s.lua_state()); color["r"] = element.color[0]; color["g"] = element.color[1]; color["b"] = element.color[2]; color["a"] = element.color[3]; return color; }, [](element& element, const sol::lua_table color) { element.color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; element.color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; element.color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; element.color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; } ); element_type["secondcolor"] = sol::property( [](element& element, const sol::this_state s) { auto color = sol::table::create(s.lua_state()); color["r"] = element.second_color[0]; color["g"] = element.second_color[1]; color["b"] = element.second_color[2]; color["a"] = element.second_color[3]; return color; }, [](element& element, const sol::lua_table color) { element.use_gradient = true; element.second_color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; element.second_color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; element.second_color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; element.second_color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; } ); element_type["usegradient"] = sol::property( [](element& element, const sol::this_state s) { return element.use_gradient; }, [](element& element, bool use_gradient) { element.use_gradient = use_gradient; } ); element_type["glowcolor"] = sol::property( [](element& element, const sol::this_state s) { auto color = sol::table::create(s.lua_state()); color["r"] = element.glow_color[0]; color["g"] = element.glow_color[1]; color["b"] = element.glow_color[2]; color["a"] = element.glow_color[3]; return color; }, [](element& element, const sol::lua_table color) { element.glow_color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; element.glow_color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; element.glow_color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; element.glow_color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; } ); element_type["backcolor"] = sol::property( [](element& element, const sol::this_state s) { auto color = sol::table::create(s.lua_state()); color["r"] = element.background_color[0]; color["g"] = element.background_color[1]; color["b"] = element.background_color[2]; color["a"] = element.background_color[3]; return color; }, [](element& element, const sol::lua_table color) { element.background_color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; element.background_color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; element.background_color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; element.background_color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; } ); element_type["bordercolor"] = sol::property( [](element& element, const sol::this_state s) { auto color = sol::table::create(s.lua_state()); color["r"] = element.border_color[0]; color["g"] = element.border_color[1]; color["b"] = element.border_color[2]; color["a"] = element.border_color[3]; return color; }, [](element& element, const sol::lua_table color) { element.border_color[0] = color["r"].get_type() == sol::type::number ? color["r"].get() : 0.f; element.border_color[1] = color["g"].get_type() == sol::type::number ? color["g"].get() : 0.f; element.border_color[2] = color["b"].get_type() == sol::type::number ? color["b"].get() : 0.f; element.border_color[3] = color["a"].get_type() == sol::type::number ? color["a"].get() : 0.f; } ); element_type["borderwidth"] = sol::property( [](element& element, const sol::this_state s) { auto color = sol::table::create(s.lua_state()); color["top"] = element.border_width[0]; color["right"] = element.border_width[1]; color["bottom"] = element.border_width[2]; color["left"] = element.border_width[3]; return color; }, [](element& element, const sol::lua_table color) { element.border_width[0] = color["top"].get_type() == sol::type::number ? color["top"].get() : 0.f; element.border_width[1] = color["right"].get_type() == sol::type::number ? color["right"].get() : element.border_width[1]; element.border_width[2] = color["bottom"].get_type() == sol::type::number ? color["bottom"].get() : element.border_width[2]; element.border_width[3] = color["left"].get_type() == sol::type::number ? color["left"].get() : element.border_width[3]; } ); element_type["font"] = sol::property( [](element& element) { return element.font; }, [](element& element, const std::string& font) { element.set_font(font); } ); element_type["fontsize"] = sol::property( [](element& element) { return element.fontsize; }, [](element& element, float fontsize) { element.fontsize = (int)fontsize; } ); element_type["onnotify"] = [&handler](element& element, const std::string& event, const event_callback& callback) { event_listener listener{}; listener.callback = callback; listener.element = &element; listener.event = event; listener.is_volatile = false; return handler.add_event_listener(std::move(listener)); }; element_type["onnotifyonce"] = [&handler](element& element, const std::string& event, const event_callback& callback) { event_listener listener{}; listener.callback = callback; listener.element = &element; listener.event = event; listener.is_volatile = true; return handler.add_event_listener(std::move(listener)); }; element_type["notify"] = [&handler](element& element, const sol::this_state s, const std::string& _event, sol::variadic_args va) { event event; event.element = &element; event.name = _event; for (auto arg : va) { event.arguments.push_back(convert({s, arg})); } notify(event); }; element_type["hidden"] = sol::property( [](element& element) { return element.hidden; }, [](element& element, bool hidden) { element.hidden = hidden; } ); element_type[sol::meta_function::new_index] = [](element& element, const sol::this_state s, const std::string& attribute, const sol::lua_value& value) { element.attributes[attribute] = convert({s, value}); }; element_type[sol::meta_function::index] = [](element& element, const sol::this_state s, const std::string& attribute) { if (element.attributes.find(attribute) == element.attributes.end()) { return sol::lua_value{s, sol::lua_nil}; } return sol::lua_value{s, convert(s, element.attributes[attribute])}; }; } void setup_menu_type(sol::state& state, event_handler& handler, scheduler& scheduler) { auto menu_type = state.new_usertype("menu"); menu_type["onnotify"] = [&handler](menu& menu, const std::string& event, const event_callback& callback) { event_listener listener{}; listener.callback = callback; listener.element = &menu; listener.event = event; listener.is_volatile = false; return handler.add_event_listener(std::move(listener)); }; menu_type["onnotifyonce"] = [&handler](menu& menu, const std::string& event, const event_callback& callback) { event_listener listener{}; listener.callback = callback; listener.element = &menu; listener.event = event; listener.is_volatile = true; return handler.add_event_listener(std::move(listener)); }; menu_type["notify"] = [&handler](menu& element, const sol::this_state s, const std::string& _event, sol::variadic_args va) { event event; event.element = &element; event.name = _event; for (auto arg : va) { event.arguments.push_back(convert({s, arg})); } notify(event); }; menu_type["addchild"] = [](const sol::this_state s, menu& menu, element& element) { menu.add_child(&element); }; menu_type["cursor"] = sol::property( [](menu& menu) { return menu.cursor; }, [](menu& menu, bool cursor) { menu.cursor = cursor; } ); menu_type["hidden"] = sol::property( [](menu& menu) { return menu.hidden; }, [](menu& menu, bool hidden) { menu.hidden = hidden; } ); menu_type["ignoreevents"] = sol::property( [](menu& menu) { return menu.ignoreevents; }, [](menu& menu, bool ignoreevents) { menu.ignoreevents = ignoreevents; } ); menu_type["isopen"] = [](menu& menu) { return menu.visible || (menu.type == menu_type::overlay && game::Menu_IsMenuOpenAndVisible(0, menu.overlay_menu.data())); }; menu_type["open"] = [&handler](menu& menu) { event event; event.element = &menu; event.name = "close"; notify(event); menu.open(); }; menu_type["close"] = [&handler](menu& menu) { event event; event.element = &menu; event.name = "close"; notify(event); menu.close(); }; menu_type["getelement"] = [](menu& menu, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { const auto value_ = convert({s, value}); for (const auto& element : menu.children) { if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { return sol::lua_value{s, element}; } } return sol::lua_value{s, sol::lua_nil}; }; menu_type["getelements"] = sol::overload ( [](menu& menu, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { const auto value_ = convert({s, value}); auto result = sol::table::create(s.lua_state()); for (const auto& element : menu.children) { if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { result.add(element); } } return result; }, [](menu& menu, const sol::this_state s) { auto result = sol::table::create(s.lua_state()); for (const auto& element : menu.children) { result.add(element); } return result; } ); } void setup_game_type(sol::state& state, event_handler& handler, scheduler& scheduler) { struct game { }; auto game_type = state.new_usertype("game_"); state["game"] = game(); game_type["getmenu"] = [](const game&, const sol::this_state s, const std::string& name) { if (menus.find(name) == menus.end()) { return sol::lua_value{s, sol::lua_nil}; } return sol::lua_value{s, &menus[name]}; }; game_type["getelement"] = [](const game&, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { const auto value_ = convert({s, value}); for (const auto& element : elements) { if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { return sol::lua_value{s, element}; } } return sol::lua_value{s, sol::lua_nil}; }; game_type["getelements"] = sol::overload ( [](const game&, const sol::this_state s, const sol::lua_value& value, const std::string& attribute) { const auto value_ = convert({s, value}); auto result = sol::table::create(s.lua_state()); for (const auto& element : elements) { if (element->attributes.find(attribute) != element->attributes.end() && element->attributes[attribute] == value_) { result.add(element); } } return result; }, [](const game&, const sol::this_state s) { auto result = sol::table::create(s.lua_state()); for (const auto& element : elements) { result.add(element); } return result; } ); game_type["time"] = []() { const auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); return now.count(); }; game_type["newmenu"] = [](const game&, const std::string& name) { menus[name] = {}; return &menus[name]; }; game_type["executecommand"] = [](const game&, const std::string& command) { command::execute(command, false); }; game_type["luiopen"] = [](const game&, const std::string& menu) { ::scheduler::once([menu]() { ::game::LUI_OpenMenu(0, menu.data(), 0, 0, 0); }, ::scheduler::pipeline::renderer); }; game_type["newmenuoverlay"] = [](const game&, const std::string& name, const std::string& menu_name) { menus[name] = {}; menus[name].type = menu_type::overlay; menus[name].overlay_menu = menu_name; return &menus[name]; }; game_type["getmouseposition"] = [](const sol::this_state s, const game&) { auto pos = sol::table::create(s.lua_state()); pos["x"] = mouse[0]; pos["y"] = mouse[1]; return pos; }; game_type["openmenu"] = [&handler](const game&, const std::string& name) { if (menus.find(name) == menus.end()) { return; } const auto menu = &menus[name]; event event; event.element = menu; event.name = "open"; notify(event); menu->open(); }; game_type["closemenu"] = [&handler](const game&, const std::string& name) { if (menus.find(name) == menus.end()) { return; } const auto menu = &menus[name]; event event; event.element = menu; event.name = "close"; notify(event); menu->close(); }; game_type["onframe"] = [&scheduler](const game&, const sol::protected_function& callback) { return scheduler.add(callback, 0, false); }; game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback, const long long milliseconds) { return scheduler.add(callback, milliseconds, true); }; game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback, const long long milliseconds) { return scheduler.add(callback, milliseconds, false); }; game_type["onnotify"] = [&handler](const game&, const std::string& event, const event_callback& callback) { event_listener listener{}; listener.callback = callback; listener.element = &ui_element; listener.event = event; listener.is_volatile = false; return handler.add_event_listener(std::move(listener)); }; game_type["onnotifyonce"] = [&handler](const game&, const std::string& event, const event_callback& callback) { event_listener listener{}; listener.callback = callback; listener.element = &ui_element; listener.event = event; listener.is_volatile = true; return handler.add_event_listener(std::move(listener)); }; game_type["isingame"] = []() { return ::game::CL_IsCgameInitialized() && ::game::g_entities[0].client; }; game_type["getdvar"] = [](const game&, const sol::this_state s, const std::string& name) { const auto dvar = ::game::Dvar_FindVar(name.data()); if (!dvar) { return sol::lua_value{s, sol::lua_nil}; } const std::string value = ::game::Dvar_ValueToString(dvar, nullptr, &dvar->current); return sol::lua_value{s, value}; }; game_type["getdvarint"] = [](const game&, const sol::this_state s, const std::string& name) { const auto dvar = ::game::Dvar_FindVar(name.data()); if (!dvar) { return sol::lua_value{s, sol::lua_nil}; } const auto value = atoi(::game::Dvar_ValueToString(dvar, nullptr, &dvar->current)); return sol::lua_value{s, value}; }; game_type["getdvarfloat"] = [](const game&, const sol::this_state s, const std::string& name) { const auto dvar = ::game::Dvar_FindVar(name.data()); if (!dvar) { return sol::lua_value{s, sol::lua_nil}; } const auto value = atof(::game::Dvar_ValueToString(dvar, nullptr, &dvar->current)); return sol::lua_value{s, value}; }; game_type["setdvar"] = [](const game&, const std::string& name, const sol::lua_value& value) { if (!valid_dvar_name(name)) { throw std::runtime_error("Invalid DVAR name, must be alphanumeric"); } const auto hash = ::game::generateHashValue(name.data()); std::string string_value; if (value.is()) { string_value = utils::string::va("%i", value.as()); } else if (value.is()) { string_value = utils::string::va("%i", value.as()); } else if (value.is()) { string_value = utils::string::va("%f", value.as()); } else if (value.is()) { const auto v = value.as(); string_value = utils::string::va("%f %f %f", v.get_x(), v.get_y(), v.get_z() ); } if (value.is()) { string_value = value.as(); } ::game::Dvar_SetCommand(hash, "", string_value.data()); }; game_type["getwindowsize"] = [](const game&, const sol::this_state s) { const auto size = ::game::ScrPlace_GetViewPlacement()->realViewportSize; auto screen = sol::table::create(s.lua_state()); screen["x"] = size[0]; screen["y"] = size[1]; return screen; }; game_type["playmenuvideo"] = [](const game&, const std::string& video) { reinterpret_cast (0x71B970_b)(video.data(), 64, 0); }; game_type[sol::meta_function::index] = [](const game&, const std::string& name) { return [name](const game&, const sol::this_state s, sol::variadic_args va) { arguments arguments{}; for (auto arg : va) { arguments.push_back(convert({s, arg})); } const auto values = call(name, arguments); std::vector returns; for (const auto& value : values) { returns.push_back(convert(s, value)); } return sol::as_returns(returns); }; }; game_type["call"] = [](const game&, const sol::this_state s, const std::string& name, sol::variadic_args va) { arguments arguments{}; for (auto arg : va) { arguments.push_back(convert({s, arg})); } const auto values = call(name, arguments); std::vector returns; for (const auto& value : values) { returns.push_back(convert(s, value)); } return sol::as_returns(returns); }; game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value) { scripting::shared_table.access([key, value](scripting::shared_table_t& table) { table[key] = value; }); }; game_type["sharedget"] = [](const game&, const std::string& key) { std::string result; scripting::shared_table.access([key, &result](scripting::shared_table_t& table) { result = table[key]; }); return result; }; game_type["sharedclear"] = [](const game&) { scripting::shared_table.access([](scripting::shared_table_t& table) { table.clear(); }); }; game_type["assetlist"] = [](const game&, const sol::this_state s, const std::string& type_string) { auto table = sol::table::create(s.lua_state()); auto index = 1; auto type_index = -1; for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++) { if (type_string == ::game::g_assetNames[i]) { type_index = i; } } if (type_index == -1) { throw std::runtime_error("Asset type does not exist"); } const auto type = static_cast<::game::XAssetType>(type_index); fastfiles::enum_assets(type, [type, &table, &index](const ::game::XAssetHeader header) { const auto asset = ::game::XAsset{type, header}; const std::string asset_name = ::game::DB_GetXAssetName(&asset); table[index++] = asset_name; }, true); return table; }; game_type["getweapondisplayname"] = [](const game&, const std::string& name) { const auto alternate = name.starts_with("alt_"); const auto weapon = ::game::G_GetWeaponForName(name.data()); char buffer[0x400]; ::game::CG_GetWeaponDisplayName(weapon, alternate, buffer, 0x400); return std::string(buffer); }; game_type["getloadedmod"] = [](const game&) { return ::game::mod_folder; }; static int request_id{}; game_type["httpget"] = [](const game&, const std::string& url) { const auto id = request_id++; ::scheduler::once([url, id]() { const auto result = utils::http::get_data(url); ::scheduler::once([result, id] { event event; event.element = &ui_element; event.name = "http_request_done"; if (result.has_value()) { event.arguments = {id, true, result.value()}; } else { event.arguments = {id, false}; } notify(event); }, ::scheduler::pipeline::renderer); }, ::scheduler::pipeline::async); return id; }; game_type["sha"] = [](const game&, const std::string& data) { return utils::string::to_upper(utils::cryptography::sha1::compute(data, true)); }; game_type["environment"] = [](const game&) { return GIT_BRANCH; }; game_type["binaryname"] = [](const game&) { char name[MAX_PATH] = {0}; GetModuleBaseName(GetCurrentProcess(), GetModuleHandle(NULL), name, MAX_PATH); return std::string(name); }; struct player { }; auto player_type = state.new_usertype("player_"); state["player"] = player(); player_type["notify"] = [](const player&, const sol::this_state s, const std::string& name, sol::variadic_args va) { if (!::game::CL_IsCgameInitialized() || !::game::g_entities[0].client) { throw std::runtime_error("Not in game"); } ::scheduler::once([s, name, args = std::vector(va.begin(), va.end())]() { try { std::vector arguments{}; for (auto arg : args) { arguments.push_back(script_convert({s, arg})); } const auto player = scripting::call("getentbynum", {0}).as(); scripting::notify(player, name, arguments); } catch (...) { } }, ::scheduler::pipeline::server); }; } void setup_lui_types(sol::state& state, event_handler& handler, scheduler& scheduler) { auto userdata_type = state.new_usertype("userdata_"); userdata_type["new"] = sol::property( [](const userdata& userdata, const sol::this_state s) { return convert(s, userdata.get("new")); }, [](const userdata& userdata, const sol::this_state s, const sol::lua_value& value) { userdata.set("new", convert({s, value})); } ); for (const auto method : methods) { const auto name = method.first; userdata_type[name] = [name](const userdata& userdata, const sol::this_state s, sol::variadic_args va) { arguments arguments{}; for (auto arg : va) { arguments.push_back(convert({s, arg})); } const auto values = call_method(userdata, name, arguments); std::vector returns; for (const auto& value : values) { returns.push_back(convert(s, value)); } return sol::as_returns(returns); }; } userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s, const std::string& name) { return convert(s, userdata.get(name)); }; userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s, const std::string& name, const sol::lua_value& value) { userdata.set(name, convert({s, value})); }; auto table_type = state.new_usertype("table_"); table_type["new"] = sol::property( [](const table& table, const sol::this_state s) { return convert(s, table.get("new")); }, [](const table& table, const sol::this_state s, const sol::lua_value& value) { table.set("new", convert({s, value})); } ); table_type["get"] = [](const table& table, const sol::this_state s, const std::string& name) { return convert(s, table.get(name)); }; table_type["set"] = [](const table& table, const sol::this_state s, const std::string& name, const sol::lua_value& value) { table.set(name, convert({s, value})); }; table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s, const std::string& name) { return convert(s, table.get(name)); }; table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s, const std::string& name, const sol::lua_value& value) { table.set(name, convert({s, value})); }; state["luiglobals"] = table((*::game::hks::lua_state)->globals.v.table); state["CoD"] = state["luiglobals"]["CoD"]; state["LUI"] = state["luiglobals"]["LUI"]; state["Engine"] = state["luiglobals"]["Engine"]; state["Game"] = state["luiglobals"]["Game"]; auto function_type = state.new_usertype("function_"); function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va) { arguments arguments{}; for (auto arg : va) { arguments.push_back(convert({s, arg})); } const auto values = function.call(arguments); std::vector returns; for (const auto& value : values) { returns.push_back(convert(s, value)); } return sol::as_returns(returns); }; state.script(animation_script); } } context::context(std::string data, script_type type) : scheduler_(state_) , event_handler_(state_) { this->state_.open_libraries(sol::lib::base, sol::lib::package, sol::lib::io, sol::lib::string, sol::lib::os, sol::lib::math, sol::lib::table); setup_io(this->state_); setup_json(this->state_); setup_vector_type(this->state_); setup_element_type(this->state_, this->event_handler_, this->scheduler_); setup_menu_type(this->state_, this->event_handler_, this->scheduler_); setup_game_type(this->state_, this->event_handler_, this->scheduler_); setup_lui_types(this->state_, this->event_handler_, this->scheduler_); if (type == script_type::file) { this->folder_ = data; this->state_["include"] = [this](const std::string& file) { this->load_script(file); }; sol::function old_require = this->state_["require"]; auto base_path = utils::string::replace(this->folder_, "/", ".") + "."; this->state_["require"] = [base_path, old_require](const std::string& path) { return old_require(base_path + path); }; this->state_["scriptdir"] = [this]() { return this->folder_; }; printf("Loading ui script '%s'\n", this->folder_.data()); this->load_script("__init__"); } if (type == script_type::code) { handle_error(this->state_.safe_script(data, &sol::script_pass_on_error)); } } context::~context() { this->state_.collect_garbage(); this->scheduler_.clear(); this->event_handler_.clear(); this->state_ = {}; } void context::run_frame() { this->scheduler_.run_frame(); this->state_.collect_garbage(); } void context::notify(const event& e) { this->scheduler_.dispatch(e); this->event_handler_.dispatch(e); } void context::load_script(const std::string& script) { if (!this->loaded_scripts_.emplace(script).second) { return; } const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string(); handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error)); } }