From c3f3e5161b62cffab6f6aef3bbe5386c2719702b Mon Sep 17 00:00:00 2001 From: Bamcane Date: Sun, 29 Mar 2026 18:11:28 +0800 Subject: [PATCH] fetch a json file to do version check instead of receiving from legacy udp versionsrv --- .github/workflows/build.yaml | 6 +- CMakeLists.txt | 12 ++- README.md | 8 +- bam.lua | 3 + bamfind/curl.lua | 48 +++++++++++ cmake/FindCurl.cmake | 27 +++++++ src/base/tl/stream.h | 2 +- src/engine/client/client.cpp | 109 +++++++++---------------- src/engine/client/client.h | 16 +--- src/engine/mapchecker.h | 1 + src/engine/shared/http_request.cpp | 125 +++++++++++++++++++++++++++++ src/engine/shared/http_request.h | 61 ++++++++++++++ src/engine/shared/mapchecker.cpp | 46 +++++++++++ src/engine/shared/mapchecker.h | 1 + src/game/variables.h | 2 +- 15 files changed, 372 insertions(+), 95 deletions(-) create mode 100644 bamfind/curl.lua create mode 100644 cmake/FindCurl.cmake create mode 100644 src/engine/shared/http_request.cpp create mode 100644 src/engine/shared/http_request.h diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d2de37d4f..617e7767d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -64,6 +64,7 @@ jobs: opus:p opusfile:p openssl:p + curl:p - name: Checkout SDL3 if: contains(matrix.os, 'ubuntu') @@ -104,7 +105,7 @@ jobs: if: contains(matrix.os, 'macOS') run: | brew update || true - brew install pkg-config sdl3 python3 ninja opus opusfile openssl || true + brew install pkg-config sdl3 python3 ninja opus opusfile openssl curl || true brew upgrade freetype sudo rm -rf /Library/Developer/CommandLineTools @@ -225,6 +226,7 @@ jobs: opus:p opusfile:p openssl:p + curl:p - name: Prepare Linux if: contains(matrix.os, 'ubuntu') @@ -248,7 +250,7 @@ jobs: if: contains(matrix.os, 'macOS') run: | brew update - brew install sdl3 opus opusfile openssl || true + brew install sdl3 opus opusfile openssl curl || true cd bam ./make_unix.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dce0c736..49cec88c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -228,6 +228,7 @@ if(NOT CMAKE_CROSSCOMPILING) endif() find_package(ZLIB) find_package(Crypto) +find_package(Curl) find_package(Freetype) find_package(Git) find_package(GTest) @@ -283,6 +284,7 @@ if(TARGET_OS AND TARGET_OS STREQUAL "mac") show_dependency_status("Hdiutil" HDIUTIL) endif() show_dependency_status("OpenSSL Crypto" CRYPTO) +show_dependency_status("Curl" CURL) show_dependency_status("Pnglite" PNGLITE) show_dependency_status("Python3" Python3) show_dependency_status("SDL3" SDL3) @@ -298,6 +300,10 @@ if(NOT(CRYPTO_FOUND)) message(SEND_ERROR "You must install OpenSSL Crypto to compile Teeworlds") endif() +if(NOT(CURL_FOUND)) + message(SEND_ERROR "You must install libcurl to compile Teeworlds") +endif() + if(CLIENT AND NOT(FREETYPE_FOUND)) message(SEND_ERROR "You must install Freetype to compile the Teeworlds client") endif() @@ -1254,6 +1260,8 @@ set_src(ENGINE_SHARED GLOB src/engine/shared engine.cpp filecollection.cpp filecollection.h + http_request.cpp + http_request.h huffman.cpp huffman.h jobs.cpp @@ -1319,7 +1327,7 @@ set(GAME_GENERATED_SHARED set(DEPS ${DEP_JSON} ${ZLIB_DEP}) # Libraries -set(LIBS ${CMAKE_THREAD_LIBS_INIT} ${ZLIB_LIBRARIES} ${CRYPTO_LIBRARIES} ${PLATFORM_LIBS}) +set(LIBS ${CMAKE_THREAD_LIBS_INIT} ${ZLIB_LIBRARIES} ${CRYPTO_LIBRARIES} ${CURL_LIBRARIES} ${PLATFORM_LIBS}) # Targets add_library(engine-shared EXCLUDE_FROM_ALL OBJECT ${ENGINE_INTERFACE} ${ENGINE_SHARED} ${ENGINE_GENERATED_SHARED} ${BASE}) @@ -1915,7 +1923,7 @@ else() list(REMOVE_DUPLICATES _DEPS) set(DEP_WHITELIST_REGEX - \"(freetype|SDL3|libcrypto|libogg|libopus|libopusfile|libopusurl)\" + \"(freetype|SDL3|libcrypto|libcurl|libogg|libopus|libopusfile|libopusurl)\" ) list(FILTER _DEPS INCLUDE REGEX \"\${DEP_WHITELIST_REGEX}\") diff --git a/README.md b/README.md index c2099bc0c..6e1b8701c 100644 --- a/README.md +++ b/README.md @@ -30,16 +30,16 @@ Dependencies sudo apt install build-essential cmake git libfreetype6-dev libsdl3-dev libpnglite-dev libopus-dev libopusfile-dev python3 libcurl4-openssl-dev # Fedora - sudo dnf install @development-tools cmake gcc-c++ git freetype-devel pnglite-devel python3 SDL3-devel opus-devel opusfile-devel openssl-devel + sudo dnf install @development-tools cmake gcc-c++ git freetype-devel pnglite-devel python3 SDL3-devel opus-devel opusfile-devel openssl-devel libcurl-devel # Arch Linux (doesn't have pnglite in its repositories) - sudo pacman -S --needed base-devel cmake freetype2 git python sdl3 opus opusfile openssl + sudo pacman -S --needed base-devel cmake freetype2 git python sdl3 opus opusfile openssl curl # macOS - brew install cmake freetype sdl3 opus opusfile openssl + brew install cmake freetype sdl3 opus opusfile openssl curl # MSYS2 (Windows) - pacman -S mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-freetype mingw-w64-ucrt-x86_64-sdl3 mingw-w64-ucrt-x86_64-zlib mingw-w64-ucrt-x86_64-opus mingw-w64-ucrt-x86_64-opusfile + pacman -S mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-freetype mingw-w64-ucrt-x86_64-sdl3 mingw-w64-ucrt-x86_64-zlib mingw-w64-ucrt-x86_64-opus mingw-w64-ucrt-x86_64-opusfile mingw-w64-ucrt-x86_64-curl ``` Downloading repository diff --git a/bam.lua b/bam.lua index 6aaffec89..ce7207953 100644 --- a/bam.lua +++ b/bam.lua @@ -2,6 +2,7 @@ CheckVersion("0.5") Import("configure.lua") Import("bamfind/crypto.lua") +Import("bamfind/curl.lua") Import("bamfind/sdl.lua") Import("bamfind/freetype.lua") Import("bamfind/opus.lua") @@ -15,6 +16,7 @@ config:Add(OptTestCompileC("minmacosxsdk", "int main(){return 0;}", "-mmacosx-ve config:Add(OptTestCompileC("buildwithoutsseflag", "#include \nint main(){_mm_pause();return 0;}", "")) config:Add(OptLibrary("zlib", "zlib.h", false)) config:Add(Crypto.OptFind("libcrypto", true)) +config:Add(Curl.OptFind("libcurl", true)) config:Add(SDL.OptFind("sdl", true)) config:Add(FreeType.OptFind("freetype", true)) config:Add(Opus.OptFind("opus", true)) @@ -359,6 +361,7 @@ end function BuildEngineCommon(settings) config.libcrypto:Apply(settings) + config.libcurl:Apply(settings) settings.link.extrafiles:Merge(Compile(settings, Collect("src/engine/shared/*.cpp"))) local c11_settings = settings:Copy(); diff --git a/bamfind/curl.lua b/bamfind/curl.lua new file mode 100644 index 000000000..dabaa784f --- /dev/null +++ b/bamfind/curl.lua @@ -0,0 +1,48 @@ +Curl = { + basepath = PathDir(ModuleFilename()), + + OptFind = function (name, required) + local check = function(option, settings) + option.value = false + option.use_pkgconfig = false + option.lib_path = nil + + if ExecuteSilent("pkg-config --exists libcurl") == 0 then + option.value = true + option.use_pkgconfig = true + end + end + + local apply = function(option, settings) + if option.use_pkgconfig == true then + settings.cc.flags:Add(RunCommand("pkg-config --cflags libcurl")) + settings.link.flags:Add(RunCommand("pkg-config --libs libcurl")) + end + end + + local save = function(option, output) + output:option(option, "value") + output:option(option, "use_pkgconfig") + end + + local display = function(option) + if option.value == true then + if option.use_pkgconfig == true then return "using pkg-config" end + return "using unknown method" + else + if option.required then + return "not found (required)" + else + return "not found (optional)" + end + end + end + + local o = MakeOption(name, 0, check, save, display) + o.Apply = apply + o.include_path = nil + o.lib_path = nil + o.required = required + return o + end +} diff --git a/cmake/FindCurl.cmake b/cmake/FindCurl.cmake new file mode 100644 index 000000000..e3b429aa5 --- /dev/null +++ b/cmake/FindCurl.cmake @@ -0,0 +1,27 @@ +if(NOT CMAKE_CROSSCOMPILING) + find_package(PkgConfig QUIET) + pkg_check_modules(PC_CURL libcurl) +endif() + +find_library(CURL_LIBRARY + NAMES curl + HINTS ${HINTS_CURL_LIBDIR} ${PC_CURL_LIBDIR} ${PC_CURL_LIBRARY_DIRS} + PATHS ${PATHS_CURL_LIBDIR} + ${CROSSCOMPILING_NO_CMAKE_SYSTEM_PATH} +) + +find_path(CURL_INCLUDEDIR curl.h + PATH_SUFFIXES curl + HINTS ${HINTS_CURL_INCLUDEDIR} ${PC_CURL_INCLUDEDIR} ${PC_CURL_INCLUDE_DIRS} + PATHS ${PATHS_CURL_INCLUDEDIR} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Curl DEFAULT_MSG CURL_LIBRARY CURL_INCLUDEDIR) + +mark_as_advanced(CURL_LIBRARY CURL_INCLUDEDIR) + +if(CURL_FOUND) + set(CURL_LIBRARIES ${CURL_LIBRARY}) + set(CURL_INCLUDE_DIRS ${CURL_INCLUDEDIR}) +endif() \ No newline at end of file diff --git a/src/base/tl/stream.h b/src/base/tl/stream.h index 104dcc41c..0a76c036b 100644 --- a/src/base/tl/stream.h +++ b/src/base/tl/stream.h @@ -85,7 +85,7 @@ class memory_stream : public stream virtual unsigned write(const unsigned char *buffer, unsigned size) { unsigned start = mem_buffer->size(); - mem_buffer->hint_size(start + size); + mem_buffer->set_size(start + size); inplace_memory_stream stream((unsigned char *) mem_buffer->base_ptr() + start, size); return stream.write(buffer, size); } diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 3a5e2082b..a30b267dd 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,8 +41,6 @@ #include #include -#include -#include #include "contacts.h" #include "serverbrowser.h" @@ -305,7 +305,7 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD m_SnapshotStorage.Init(); m_ReceivedSnapshots = 0; - m_VersionInfo.m_State = CVersionInfo::STATE_INIT; + m_pVersionRequest = 0; m_pLocalServerProcess = 0; } @@ -1017,55 +1017,6 @@ int CClient::UnpackServerInfoPlayer(CUnpacker *pUnpacker, CServerInfo *pInfo, in void CClient::ProcessConnlessPacket(CNetChunk *pPacket) { - // version server - if(m_VersionInfo.m_State == CVersionInfo::STATE_READY && net_addr_comp(&pPacket->m_Address, &m_VersionInfo.m_VersionServeraddr.m_Addr, true) == 0) - { - // version info - if(pPacket->m_DataSize == (int) (sizeof(VERSIONSRV_VERSION) + sizeof(GAME_RELEASE_VERSION)) && - mem_comp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) - - { - char *pVersionData = (char *) pPacket->m_pData + sizeof(VERSIONSRV_VERSION); - int VersionMatch = !mem_comp(pVersionData, GAME_RELEASE_VERSION, sizeof(GAME_RELEASE_VERSION)); - - char aVersion[sizeof(GAME_RELEASE_VERSION)]; - str_copy(aVersion, pVersionData, sizeof(aVersion)); - - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "version does %s (%s)", - VersionMatch ? "match" : "NOT match", - aVersion); - m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/version", aBuf); - - // assume version is out of date when version-data doesn't match - if(!VersionMatch) - { - str_copy(m_aVersionStr, aVersion, sizeof(m_aVersionStr)); - } - - // request the map version list now - unsigned char aData[sizeof(VERSIONSRV_GETMAPLIST) + sizeof(unsigned)]; - mem_copy(aData, VERSIONSRV_GETMAPLIST, sizeof(VERSIONSRV_GETMAPLIST)); - uint_to_bytes_be(aData + sizeof(VERSIONSRV_GETMAPLIST), CLIENT_VERSION); - CNetChunk Packet; - Packet.m_ClientID = -1; - Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr; - Packet.m_Flags = NETSENDFLAG_CONNLESS; - Packet.m_pData = aData; - Packet.m_DataSize = sizeof(aData); - m_ContactClient.Send(&Packet); - } - - // map version list - if(pPacket->m_DataSize >= (int) sizeof(VERSIONSRV_MAPLIST) && - mem_comp(pPacket->m_pData, VERSIONSRV_MAPLIST, sizeof(VERSIONSRV_MAPLIST)) == 0) - { - m_pMapChecker->AddMaplist( - (const CMapVersion *) ((char *) pPacket->m_pData + sizeof(VERSIONSRV_MAPLIST)), - unsigned(pPacket->m_DataSize - sizeof(VERSIONSRV_MAPLIST)) / sizeof(CMapVersion)); - } - } - // server list from master server if(pPacket->m_DataSize >= (int) sizeof(SERVERBROWSE_LIST) && mem_comp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) @@ -1788,35 +1739,46 @@ void CClient::Update() void CClient::VersionUpdate() { - if(m_VersionInfo.m_State == CVersionInfo::STATE_INIT) + if(!m_pVersionRequest) { - Engine()->HostLookup(&m_VersionInfo.m_VersionServeraddr, Config()->m_ClVersionServer, m_ContactClient.NetType()); - m_VersionInfo.m_State = CVersionInfo::STATE_START; + m_pVersionRequest = new CHttpRequest("GET", Config()->m_ClVersionUrl, 0L); + m_pVersionRequest->StartRun(Engine()); } - else if(m_VersionInfo.m_State == CVersionInfo::STATE_START) + else if(m_pVersionRequest->Status() == CJob::STATE_DONE && !m_pVersionRequest->IsChecked()) { - if(m_VersionInfo.m_VersionServeraddr.m_Job.Status() == CJob::STATE_DONE) + if(m_pVersionRequest->Result() == 0 && m_pVersionRequest->ResponseCode() == 200) { - if(m_VersionInfo.m_VersionServeraddr.m_Job.Result() == 0) + CJsonParser Parser; + const json_value *pJsonData = Parser.ParseData(m_pVersionRequest->ReceivedData(), m_pVersionRequest->ReceivedDataSize()); + if(pJsonData) { - CNetChunk Packet; - - mem_zero(&Packet, sizeof(Packet)); + if((*pJsonData)["version"].type == json_string) + { + const char *pVersion = (*pJsonData)["version"]; + bool VersionMatch = str_comp(GAME_RELEASE_VERSION, pVersion) == 0; - m_VersionInfo.m_VersionServeraddr.m_Addr.port = VERSIONSRV_PORT; + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "version does %s (%s)", + VersionMatch ? "match" : "NOT match", + pVersion); - Packet.m_ClientID = -1; - Packet.m_Address = m_VersionInfo.m_VersionServeraddr.m_Addr; - Packet.m_pData = VERSIONSRV_GETVERSION; - Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION); - Packet.m_Flags = NETSENDFLAG_CONNLESS; + m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/version", aBuf); - m_ContactClient.Send(&Packet); - m_VersionInfo.m_State = CVersionInfo::STATE_READY; + // assume version is out of date when version-data doesn't match + if(!VersionMatch) + str_copy(m_aVersionStr, pVersion, sizeof(m_aVersionStr)); + } + if((*pJsonData)["maps"].type == json_array) + m_pMapChecker->AddMaplist(&(*pJsonData)["maps"]); } else - m_VersionInfo.m_State = CVersionInfo::STATE_ERROR; + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "failed to load version.json, json error: %s", Parser.Error()); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/version", aBuf); + } } + m_pVersionRequest->MarkAsChecked(); } } @@ -2207,6 +2169,9 @@ void CClient::Run() m_ServerBrowser.SaveServerlist(); CloseLocalServer(); + + if(m_pVersionRequest) + delete m_pVersionRequest; // shutdown SDL SDL_Quit(); } @@ -2663,6 +2628,10 @@ int main(int argc, const char **argv) return -1; } + CCurlInit Curl; + if(Curl.IsFailed()) + return -1; + signal(SIGINT, HandleSigIntTerm); signal(SIGTERM, HandleSigIntTerm); diff --git a/src/engine/client/client.h b/src/engine/client/client.h index ef21c11b5..550a9c7a3 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -172,21 +172,7 @@ class CClient : public IClient, public CDemoPlayer::IListener // class CServerInfo m_CurrentServerInfo; - - // version info - struct CVersionInfo - { - enum - { - STATE_INIT = 0, - STATE_START, - STATE_READY, - STATE_ERROR, - }; - - int m_State; - class CHostLookup m_VersionServeraddr; - } m_VersionInfo; + class CHttpRequest *m_pVersionRequest; int64 TickStartTime(int Tick); diff --git a/src/engine/mapchecker.h b/src/engine/mapchecker.h index f7a28383d..d9a154544 100644 --- a/src/engine/mapchecker.h +++ b/src/engine/mapchecker.h @@ -11,6 +11,7 @@ class IMapChecker : public IInterface { MACRO_INTERFACE("mapchecker", 0) public: + virtual void AddMaplist(const struct _json_value *pMaplist) = 0; virtual void AddMaplist(const struct CMapVersion *pMaplist, unsigned Num) = 0; virtual bool IsMapValid(const char *pMapName, const SHA256_DIGEST *pMapSha256, unsigned MapCrc, unsigned MapSize) = 0; virtual bool ReadAndValidateMap(const char *pFilename, int StorageType) = 0; diff --git a/src/engine/shared/http_request.cpp b/src/engine/shared/http_request.cpp new file mode 100644 index 000000000..7cf7d344e --- /dev/null +++ b/src/engine/shared/http_request.cpp @@ -0,0 +1,125 @@ +#include +#include +#include + +#include "http_request.h" + +#include + +CCurlInit::CCurlInit() +{ + if(curl_global_init(CURL_GLOBAL_ALL)) + { + dbg_msg("http", "failed to init curl"); + m_Failed = true; + } + else + { + m_Failed = false; + } +} + +CCurlInit::~CCurlInit() +{ + curl_global_cleanup(); +} + +void EscapeUrl(char *pBuf, int Size, const char *pStr) +{ + char *pEsc = curl_easy_escape(nullptr, pStr, 0); + str_copy(pBuf, pEsc, Size); + curl_free(pEsc); +} + +CHttpRequest::CHttpRequest(const char *pRequest, const char *pUrl, long TimeoutSeconds) +{ + str_copy(m_aRequest, pRequest, sizeof(m_aRequest)); + str_copy(m_aUrl, pUrl, sizeof(m_aUrl)); + + m_pHeaderList = 0; + + m_IsChecked = false; + m_TimeoutSeconds = TimeoutSeconds; + m_PostData.clear(); +} + +void CHttpRequest::PostData(const unsigned char *pPost, int Size) +{ + memory_stream Stream(&m_PostData); + Stream.write(pPost, Size); +} + +void CHttpRequest::PostJson(const char *pJson) +{ + PostData((const unsigned char *) pJson, str_length(pJson) + 1); + AddHeader("Content-Type: application/json"); +} + +void CHttpRequest::AddHeader(const char *pHeader) +{ + m_pHeaderList = curl_slist_append(m_pHeaderList, pHeader); +} + +// for windows build +#ifdef AddJob +#undef AddJob +#endif + +void CHttpRequest::StartRun(IEngine *pEngine) +{ + pEngine->AddJob(&m_Job, CHttpRequest::Run, this); +} + +size_t CHttpRequest::WriteCallback(char *pData, size_t Size, size_t Number, void *pUser) +{ + CHttpRequest *pRequest = static_cast(pUser); + size_t TotalSize = Size * Number; + memory_stream Stream(&pRequest->m_ReceivedData); + Stream.write((const unsigned char *) pData, TotalSize); + return TotalSize; +} + +int CHttpRequest::Run(void *pUser) +{ + CHttpRequest *pRequest = static_cast(pUser); + pRequest->m_pHandle = curl_easy_init(); + if(!pRequest->m_pHandle) + return -1; + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_URL, pRequest->m_aUrl); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_CUSTOMREQUEST, pRequest->m_aRequest); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_PROTOCOLS_STR, "https"); + + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_MAXREDIRS, 5L); + + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_CONNECTTIMEOUT, 5L); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_TIMEOUT, pRequest->m_TimeoutSeconds); + + if(pRequest->m_PostData.size() > 0) + { + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_POSTFIELDS, (const char*) pRequest->m_PostData.base_ptr()); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_POSTFIELDSIZE, pRequest->m_PostData.size()); + } + + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_USERAGENT, "TeeworldsArchive " GAME_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")"); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_HTTPHEADER, pRequest->m_pHeaderList); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_ACCEPT_ENCODING, ""); + + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_WRITEFUNCTION, CHttpRequest::WriteCallback); + curl_easy_setopt(pRequest->m_pHandle, CURLOPT_WRITEDATA, pRequest); + + CURLcode Result = curl_easy_perform(pRequest->m_pHandle); + if(Result != CURLE_OK) + dbg_msg("http", "libcurl error (%u): %s", Result, curl_easy_strerror(Result)); + + { + long ResponseCode; + curl_easy_getinfo(pRequest->m_pHandle, CURLINFO_RESPONSE_CODE, &ResponseCode); + pRequest->m_ResponseCode = ResponseCode; + } + + curl_slist_free_all(pRequest->m_pHeaderList); + curl_easy_cleanup(pRequest->m_pHandle); + return Result != CURLE_OK; +} diff --git a/src/engine/shared/http_request.h b/src/engine/shared/http_request.h new file mode 100644 index 000000000..2f2f034f9 --- /dev/null +++ b/src/engine/shared/http_request.h @@ -0,0 +1,61 @@ +#ifndef ENGINE_SHARED_HTTP_REQUEST_H +#define ENGINE_SHARED_HTTP_REQUEST_H + +#include +#include +#include "jobs.h" + +extern "C" +{ + struct curl_slist; +} + +class CHttpRequest +{ + char m_aRequest[16]; + char m_aUrl[256]; + long m_TimeoutSeconds; + + array m_PostData; + array m_ReceivedData; + + int m_ResponseCode; + + CJob m_Job; + void *m_pHandle; + curl_slist *m_pHeaderList; + + bool m_IsChecked; + + static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser); + static int Run(void *pUser); +public: + CHttpRequest(const char *pRequest, const char *pUrl, long TimeoutSeconds); + void PostData(const unsigned char *pPost, int Size); + void PostJson(const char *pJson); + void AddHeader(const char *pHeader); + void StartRun(class IEngine *pEngine); + + void MarkAsChecked() { m_IsChecked = true; } + + bool IsChecked() const { return m_IsChecked; } + int Status() const { return m_Job.Status(); } + int Result() const { return m_Job.Result(); } + int ResponseCode() const { return m_ResponseCode; } + const unsigned char *ReceivedData() const { return m_ReceivedData.base_ptr(); } + int ReceivedDataSize() const { return m_ReceivedData.size(); } +}; + +class CCurlInit +{ + bool m_Failed; +public: + CCurlInit(); + ~CCurlInit(); + + bool IsFailed() const { return m_Failed; } +}; + +void EscapeUrl(char *pBuf, int Size, const char *pStr); + +#endif // ENGINE_SHARED_HTTP_REQUEST_H diff --git a/src/engine/shared/mapchecker.cpp b/src/engine/shared/mapchecker.cpp index 6d1cf8993..51f53bab7 100644 --- a/src/engine/shared/mapchecker.cpp +++ b/src/engine/shared/mapchecker.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -16,6 +17,51 @@ CMapChecker::CMapChecker() SetDefaults(); } +void CMapChecker::AddMaplist(const json_value *pMapList) +{ + if(!pMapList || pMapList->type != json_array) + return; + if(m_ClearListBeforeAdding) + Init(); + const json_value &rStart = *pMapList; + for(unsigned i = 0; i < rStart.u.array.length; i++) + { + const json_value &rMap = rStart[i]; + + // read map name + if(rMap["maps"].type != json_string) + continue; + const char *pMapName = rMap["maps"]; + + // read map size + if(rMap["size"].type != json_integer) + continue; + int64 Size = (json_int_t) rMap["size"]; + + // read crc + if(rMap["crc"].type != json_integer) + continue; + int64 Crc = (json_int_t) rMap["crc"]; + + // read sha256 + if(rMap["sha256"].type != json_string) + continue; + const char *pSha256 = rMap["sha256"]; + SHA256_DIGEST Sha256; + if(sha256_from_str(&Sha256, pSha256) == -1) + continue; + + CWhitelistEntry *pEntry = (CWhitelistEntry *) m_Whitelist.Allocate(sizeof(CWhitelistEntry)); + pEntry->m_pNext = m_pFirst; + m_pFirst = pEntry; + + str_copy(pEntry->m_aMapName, pMapName, sizeof(pEntry->m_aMapName)); + pEntry->m_MapCrc = Crc; + pEntry->m_MapSize = Size; + pEntry->m_MapSha256 = Sha256; + } +} + void CMapChecker::Init() { m_Whitelist.Reset(); diff --git a/src/engine/shared/mapchecker.h b/src/engine/shared/mapchecker.h index 692bd2501..9032b63e2 100644 --- a/src/engine/shared/mapchecker.h +++ b/src/engine/shared/mapchecker.h @@ -33,6 +33,7 @@ class CMapChecker : public IMapChecker public: CMapChecker(); + void AddMaplist(const struct _json_value *pMapList); void AddMaplist(const struct CMapVersion *pMaplist, unsigned Num); bool IsMapValid(const char *pMapName, const SHA256_DIGEST *pMapSha256, unsigned MapCrc, unsigned MapSize); bool ReadAndValidateMap(const char *pFilename, int StorageType); diff --git a/src/game/variables.h b/src/game/variables.h index 70b4b0ca4..8ebf53f55 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -58,7 +58,7 @@ MACRO_CONFIG_INT(ClMotdTime, cl_motd_time, 10, 0, 100, CFGFLAG_CLIENT | CFGFLAG_ MACRO_CONFIG_INT(ClShowXmasHats, cl_show_xmas_hats, 1, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "0=never, 1=during christmas, 2=always") MACRO_CONFIG_INT(ClShowEasterEggs, cl_show_easter_eggs, 1, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "0=never, 1=during easter, 2=always") -MACRO_CONFIG_STR(ClVersionServer, cl_version_server, 100, "version.teeworlds.wiki", CFGFLAG_CLIENT | CFGFLAG_SAVE, "Server to use to check for new versions") +MACRO_CONFIG_STR(ClVersionUrl, cl_version_url, 100, "https://archive.teeworlds.wiki/version.json", CFGFLAG_CLIENT | CFGFLAG_SAVE, "Url to use to check for new versions") MACRO_CONFIG_STR(ClLanguagefile, cl_languagefile, 255, "", CFGFLAG_CLIENT | CFGFLAG_SAVE, "What language file to use")