From b618def7a3dc9e7c192ce9573b3cf43d987c4f93 Mon Sep 17 00:00:00 2001 From: Federico Cecchetto Date: Sun, 26 Jun 2022 23:07:25 +0200 Subject: [PATCH] Use curl --- .gitmodules | 3 + deps/curl | 1 + deps/premake/curl.lua | 73 +++++++++++++++++++++++++ src/client/component/discord.cpp | 26 +++++++-- src/client/component/server_list.cpp | 17 ++++++ src/client/component/updater.cpp | 36 +++++++++--- src/common/utils/http.cpp | 82 ++++++++++++++++++---------- src/common/utils/http.hpp | 13 ++++- 8 files changed, 207 insertions(+), 44 deletions(-) create mode 160000 deps/curl create mode 100644 deps/premake/curl.lua diff --git a/.gitmodules b/.gitmodules index e92c76fd..af79585d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -45,3 +45,6 @@ path = deps/zlib url = https://github.com/madler/zlib.git branch = develop +[submodule "deps/curl"] + path = deps/curl + url = https://github.com/curl/curl.git diff --git a/deps/curl b/deps/curl new file mode 160000 index 00000000..d64115d7 --- /dev/null +++ b/deps/curl @@ -0,0 +1 @@ +Subproject commit d64115d7bb8ae4c136b620912da523c063f1d2ee diff --git a/deps/premake/curl.lua b/deps/premake/curl.lua new file mode 100644 index 00000000..8db164e1 --- /dev/null +++ b/deps/premake/curl.lua @@ -0,0 +1,73 @@ +curl = { + source = path.join(dependencies.basePath, "curl"), +} + +function curl.import() + links { "curl" } + + filter "toolset:msc*" + links { "Crypt32.lib" } + filter {} + + curl.includes() +end + +function curl.includes() +filter "toolset:msc*" + includedirs { + path.join(curl.source, "include"), + } + + defines { + "CURL_STRICTER", + "CURL_STATICLIB", + "CURL_DISABLE_LDAP", + } +filter {} +end + +function curl.project() + if not os.istarget("windows") then + return + end + + project "curl" + language "C" + + curl.includes() + + includedirs { + path.join(curl.source, "lib"), + } + + files { + path.join(curl.source, "lib/**.c"), + path.join(curl.source, "lib/**.h"), + } + + defines { + "BUILDING_LIBCURL", + } + + filter "toolset:msc*" + + defines { + "USE_SCHANNEL", + "USE_WINDOWS_SSPI", + "USE_THREADS_WIN32", + } + + filter "toolset:not msc*" + + defines { + "USE_GNUTLS", + "USE_THREADS_POSIX", + } + + filter {} + + warnings "Off" + kind "StaticLib" +end + +table.insert(dependencies, curl) \ No newline at end of file diff --git a/src/client/component/discord.cpp b/src/client/component/discord.cpp index e0693fa5..2495b7bd 100644 --- a/src/client/component/discord.cpp +++ b/src/client/component/discord.cpp @@ -138,21 +138,37 @@ namespace discord { const auto data = utils::http::get_data( utils::string::va(AVATAR_URL, id.data(), avatar.data())); - if (data.has_value()) + if (!data.has_value()) { - materials::add(utils::string::va(AVATAR, id.data()), data.value()); + return; } + + const auto& value = data.value(); + if (value.code != CURLE_OK) + { + return; + } + + materials::add(utils::string::va(AVATAR, id.data()), value.buffer); } bool has_default_avatar = false; void download_default_avatar() { const auto data = utils::http::get_data(DEFAULT_AVATAR_URL); - if (data.has_value()) + if (!data.has_value()) { - has_default_avatar = true; - materials::add(DEFAULT_AVATAR, data.value()); + return; } + + const auto& value = data.value(); + if (value.code != CURLE_OK) + { + return; + } + + has_default_avatar = true; + materials::add(DEFAULT_AVATAR, value.buffer); } } diff --git a/src/client/component/server_list.cpp b/src/client/component/server_list.cpp index f5059e35..c74c259c 100644 --- a/src/client/component/server_list.cpp +++ b/src/client/component/server_list.cpp @@ -375,6 +375,23 @@ namespace server_list insert_server(std::move(server)); } + int get_player_count() + { + std::lock_guard _(mutex); + auto count = 0; + for (const auto& server : servers) + { + count += server.clients - server.bots; + } + return count; + } + + int get_server_count() + { + std::lock_guard _(mutex); + return static_cast(servers.size()); + } + class component final : public component_interface { public: diff --git a/src/client/component/updater.cpp b/src/client/component/updater.cpp index ab2c748a..044c81b4 100644 --- a/src/client/component/updater.cpp +++ b/src/client/component/updater.cpp @@ -26,9 +26,9 @@ #define DATA_PATH "data/" #define DATA_PATH_DEV "data-dev/" -#define ERR_UPDATE_CHECK_FAIL "Failed to check for updates" +#define ERR_UPDATE_CHECK_FAIL "Failed to check for updates:\n%s" #define ERR_UPDATE_CHECK_FAIL_BAD_RESPONSE "Bad response" -#define ERR_DOWNLOAD_FAIL "Failed to download file " +#define ERR_DOWNLOAD_FAIL "Failed to download file %s:\n%s" #define ERR_WRITE_FAIL "Failed to write file " #define BINARY_NAME "h1-mod.exe" @@ -139,7 +139,7 @@ namespace updater return utils::string::va("%i", uint32_t(time(nullptr))); } - std::optional download_file(const std::string& name) + std::optional download_file(const std::string& name) { return utils::http::get_data(MASTER + select(DATA_PATH, DATA_PATH_DEV) + name + "?" + get_time_str()); } @@ -191,6 +191,12 @@ namespace updater return {}; } + + std::string curl_error(CURLcode code) + { + const auto str_error = curl_easy_strerror(code); + return utils::string::va("%s (%i)", str_error, code); + } } // workaround @@ -337,12 +343,20 @@ namespace updater if (!files_data.has_value()) { - set_update_check_status(true, false, ERR_UPDATE_CHECK_FAIL); + set_update_check_status(true, false, utils::string::va(ERR_UPDATE_CHECK_FAIL, "Unknown error")); + return; + } + + const auto& value = files_data.value(); + if (value.code != CURLE_OK) + { + const auto error = curl_error(value.code); + set_update_check_status(true, false, utils::string::va(ERR_UPDATE_CHECK_FAIL, error.data())); return; } rapidjson::Document j; - j.Parse(files_data.value().data()); + j.Parse(value.buffer.data()); if (!j.IsArray()) { @@ -433,11 +447,19 @@ namespace updater if (!data.has_value()) { - set_update_download_status(true, false, ERR_DOWNLOAD_FAIL + file); + set_update_download_status(true, false, utils::string::va(ERR_DOWNLOAD_FAIL, file.data(), "Unknown error")); return; } - downloads.push_back({file, data.value()}); + const auto& value = data.value(); + if (value.code != CURLE_OK) + { + const auto error = curl_error(value.code); + set_update_download_status(true, false, utils::string::va(ERR_DOWNLOAD_FAIL, file.data(), error.data())); + return; + } + + downloads.push_back({file, value.buffer}); } for (const auto& download : downloads) diff --git a/src/common/utils/http.cpp b/src/common/utils/http.cpp index 3cb59991..a3259323 100644 --- a/src/common/utils/http.cpp +++ b/src/common/utils/http.cpp @@ -4,41 +4,63 @@ namespace utils::http { - std::optional get_data(const std::string& url) + namespace { - CComPtr stream; - - if (FAILED(URLOpenBlockingStreamA(nullptr, url.data(), &stream, 0, nullptr))) + size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp) { - return {}; + auto* buffer = static_cast(userp); + + const auto total_size = size * nmemb; + buffer->append(static_cast(contents), total_size); + return total_size; } - - char buffer[0x1000]; - std::string result; - - HRESULT status{}; - - do - { - DWORD bytes_read = 0; - status = stream->Read(buffer, sizeof(buffer), &bytes_read); - - if (bytes_read > 0) - { - result.append(buffer, bytes_read); - } - } - while (SUCCEEDED(status) && status != S_FALSE); - - if (FAILED(status)) - { - return {}; - } - - return {result}; } - std::future> get_data_async(const std::string& url) + std::optional get_data(const std::string& url) + { + curl_slist* header_list = nullptr; + auto* curl = curl_easy_init(); + if (!curl) + { + return {}; + } + + auto _ = gsl::finally([&]() + { + curl_slist_free_all(header_list); + curl_easy_cleanup(curl); + }); + + std::string buffer{}; + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list); + curl_easy_setopt(curl, CURLOPT_URL, url.data()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); + + const auto code = curl_easy_perform(curl); + + if (code == CURLE_OK) + { + result result; + result.code = code; + result.buffer = std::move(buffer); + + return result; + } + else + { + result result; + result.code = code; + + return result; + } + + return {}; + } + + std::future> get_data_async(const std::string& url) { return std::async(std::launch::async, [url]() { diff --git a/src/common/utils/http.hpp b/src/common/utils/http.hpp index 65628a9f..7361a7b2 100644 --- a/src/common/utils/http.hpp +++ b/src/common/utils/http.hpp @@ -3,9 +3,18 @@ #include #include #include +#include + +#include namespace utils::http { - std::optional get_data(const std::string& url); - std::future> get_data_async(const std::string& url); + struct result + { + CURLcode code; + std::string buffer; + }; + + std::optional get_data(const std::string& url); + std::future> get_data_async(const std::string& url); }