diff --git a/data/cdata/ui_scripts/mods/download.lua b/data/cdata/ui_scripts/mods/download.lua index cac8cff4..b530fc42 100644 --- a/data/cdata/ui_scripts/mods/download.lua +++ b/data/cdata/ui_scripts/mods/download.lua @@ -15,7 +15,6 @@ Engine.GetLuiRoot():registerEventHandler("mod_download_start", function(element, end) popup:registerEventHandler("mod_download_progress", function(element, event) - print(event.fraction * 100) popup.text:setText(string.format("Downloading %s (%i%%)...", file, math.floor(event.fraction * 100))) end) diff --git a/data/cdata/ui_scripts/server_list/__init__.lua b/data/cdata/ui_scripts/server_list/__init__.lua index 94df0df8..b17a57f4 100644 --- a/data/cdata/ui_scripts/server_list/__init__.lua +++ b/data/cdata/ui_scripts/server_list/__init__.lua @@ -4,3 +4,4 @@ end require("lobby") require("serverlist") +require("confirm") diff --git a/data/cdata/ui_scripts/server_list/confirm.lua b/data/cdata/ui_scripts/server_list/confirm.lua new file mode 100644 index 00000000..da79dadc --- /dev/null +++ b/data/cdata/ui_scripts/server_list/confirm.lua @@ -0,0 +1,12 @@ +LUI.MenuBuilder.m_types_build["popup_confirmdownload"] = function() + return LUI.MenuBuilder.BuildRegisteredType("generic_yesno_popup", { + popup_title = Engine.Localize("@MENU_NOTICE"), + message_text = Engine.Localize("@LUA_MENU_3RD_PARTY_CONTENT_DESC", download.getwwwurl()), + yes_action = function() + download.userdownloadresponse(true) + end, + no_action = function() + download.userdownloadresponse(false) + end + }) +end diff --git a/data/zonetool/h1_mod_common/localizedstrings/english.json b/data/zonetool/h1_mod_common/localizedstrings/english.json index c07118fd..caebe8bb 100644 --- a/data/zonetool/h1_mod_common/localizedstrings/english.json +++ b/data/zonetool/h1_mod_common/localizedstrings/english.json @@ -62,6 +62,8 @@ "MPHUD_LATENCY_MS": " ms", "LUA_MENU_TELEMETRY": "TELEMETRY", + "LUA_MENU_3RD_PARTY_CONTENT_DESC": "Would you like to install required 3rd-party content for this server? (from &&1)", + "MENU_ENGLISH": "English", "MENU_ENGLISH_SAFE": "English (Safe)", "MENU_FRENCH": "Français", diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 4f8a3f34..6962600f 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -11,9 +11,11 @@ #include "fastfiles.hpp" #include "game/game.hpp" +#include "game/ui_scripting/execution.hpp" #include "steam/steam.hpp" +#include #include #include #include @@ -48,6 +50,12 @@ namespace party {".arena", "usermaparenahash", true}, }; + struct + { + game::netadr_s host{}; + utils::info_string info_string{}; + } saved_info_response; + void perform_game_initialization() { command::execute("onlinegame 1", true); @@ -297,9 +305,80 @@ namespace party return false; } + void close_joining_popups() + { + if (game::Menu_IsMenuOpenAndVisible(0, "popup_acceptinginvite")) + { + command::execute("lui_close popup_acceptinginvite", false); + } + if (game::Menu_IsMenuOpenAndVisible(0, "generic_waiting_popup_")) + { + command::execute("lui_close generic_waiting_popup_", false); + } + } + + std::string get_whitelist_json_path() + { + return (utils::properties::get_appdata_path() / "whitelist.json").generic_string(); + } + + nlohmann::json get_whitelist_json_object() + { + std::string data; + if (!utils::io::read_file(get_whitelist_json_path(), &data)) + { + return nullptr; + } + + nlohmann::json obj; + try + { + obj = nlohmann::json::parse(data.data()); + } + catch (const nlohmann::json::parse_error& ex) + { + menu_error(utils::string::va("%s\n", ex.what())); + return nullptr; + } + + return obj; + } + + std::string target_ip_to_string(const game::netadr_s& target) + { + return utils::string::va("%i.%i.%i.%i", + static_cast(saved_info_response.host.ip[0]), + static_cast(saved_info_response.host.ip[1]), + static_cast(saved_info_response.host.ip[2]), + static_cast(saved_info_response.host.ip[3])); + } + + bool download_files(const game::netadr_s& target, const utils::info_string& info, bool allow_download); + + bool should_user_confirm(const game::netadr_s& target) + { + nlohmann::json obj = get_whitelist_json_object(); + if (obj != nullptr) + { + const auto target_ip = target_ip_to_string(target); + for (const auto& [key, value] : obj.items()) + { + if (value.is_string() && value.get() == target_ip) + { + return false; + } + } + } + + close_joining_popups(); + command::execute("lui_open_popup popup_confirmdownload", false); + + return true; + } + bool needs_vid_restart = false; - bool download_files(const game::netadr_s& target, const utils::info_string& info) + bool download_files(const game::netadr_s& target, const utils::info_string& info, bool allow_download) { try { @@ -311,6 +390,11 @@ namespace party if (files.size() > 0) { + if (!allow_download && should_user_confirm(target)) + { + return true; + } + download::stop_download(); download::start_download(target, info, files); return true; @@ -437,18 +521,36 @@ namespace party } } + std::string get_www_url() + { + return saved_info_response.info_string.get("sv_wwwBaseUrl"); + } + + void user_download_response(bool response) + { + if (!response) + { + return; + } + + nlohmann::json obj = get_whitelist_json_object(); + if (obj == nullptr) + { + obj = {}; + } + + obj.push_back(target_ip_to_string(saved_info_response.host)); + + utils::io::write_file(get_whitelist_json_path(), obj.dump(4)); + + download_files(saved_info_response.host, saved_info_response.info_string, true); + } + void menu_error(const std::string& error) { console::error("%s\n", error.data()); - if (game::Menu_IsMenuOpenAndVisible(0, "popup_acceptinginvite")) - { - command::execute("lui_close popup_acceptinginvite", false); - } - if (game::Menu_IsMenuOpenAndVisible(0, "generic_waiting_popup_")) - { - command::execute("lui_close generic_waiting_popup_", false); - } + close_joining_popups(); utils::hook::invoke(0x17D770_b, error.data(), "MENU_NOTICE"); // Com_SetLocalizedErrorMessage *reinterpret_cast(0x2ED2F78_b) = 1; @@ -930,6 +1032,10 @@ namespace party return; } + saved_info_response = {}; + saved_info_response.host = target; + saved_info_response.info_string = info; + if (info.get("challenge") != connect_state.challenge) { menu_error("Connection failed: Invalid challenge."); @@ -971,7 +1077,7 @@ namespace party return; } - if (download_files(target, info)) + if (download_files(target, info, false)) { return; } diff --git a/src/client/component/party.hpp b/src/client/component/party.hpp index 69590895..ca4852b2 100644 --- a/src/client/component/party.hpp +++ b/src/client/component/party.hpp @@ -3,6 +3,9 @@ namespace party { + std::string get_www_url(); + void user_download_response(bool response); + void menu_error(const std::string& error); void reset_connect_state(); diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index 2089a5ac..7e2e0d5e 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -19,6 +19,7 @@ #include "scripting.hpp" #include "updater.hpp" #include "server_list.hpp" +#include "party.hpp" #include "game/ui_scripting/execution.hpp" #include "game/scripting/execution.hpp" @@ -69,12 +70,6 @@ namespace ui_scripting return itr == globals.loaded_scripts.end() ? std::string() : itr->second; } - table get_globals() - { - const auto state = *game::hks::lua_state; - return state->globals.v.table; - } - void print_error(const std::string& error) { console::error("************** LUI script execution error **************\n"); @@ -369,6 +364,9 @@ namespace ui_scripting lua["download"] = download_table; download_table["abort"] = download::stop_download; + + download_table["userdownloadresponse"] = party::user_download_response; + download_table["getwwwurl"] = party::get_www_url; } void start() @@ -523,6 +521,12 @@ namespace ui_scripting } } + table get_globals() + { + const auto state = *game::hks::lua_state; + return state->globals.v.table; + } + template game::hks::cclosure* convert_function(F f) { diff --git a/src/client/component/ui_scripting.hpp b/src/client/component/ui_scripting.hpp index 804c8a14..2d01867b 100644 --- a/src/client/component/ui_scripting.hpp +++ b/src/client/component/ui_scripting.hpp @@ -42,6 +42,8 @@ namespace ui_scripting return wrap_function(std::function(f)); } + table get_globals(); + template game::hks::cclosure* convert_function(F f); diff --git a/src/client/game/ui_scripting/execution.cpp b/src/client/game/ui_scripting/execution.cpp index efce4474..acf281c5 100644 --- a/src/client/game/ui_scripting/execution.cpp +++ b/src/client/game/ui_scripting/execution.cpp @@ -111,8 +111,7 @@ namespace ui_scripting try { - const auto globals = table((*::game::hks::lua_state)->globals.v.table); - const auto engine = globals.get("Engine").as(); + const auto engine = get_globals().get("Engine").as
(); const auto root = engine.get("GetLuiRoot")()[0].as(); const auto process_event = root.get("processEvent"); diff --git a/src/common/utils/io.hpp b/src/common/utils/io.hpp index 19e8c143..ee5998db 100644 --- a/src/common/utils/io.hpp +++ b/src/common/utils/io.hpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace utils::io {