From 91eb1966a03371e50394f37cdfd9ee570b895182 Mon Sep 17 00:00:00 2001 From: Agent X <44549182+Agent-11@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:33:43 -0500 Subject: [PATCH] [WIP] Update checker --- Makefile | 9 +- autogen/lua_definitions/constants.lua | 2 +- lang/English.ini | 3 + src/pc/crash_handler.c | 2 +- src/pc/djui/djui_panel_host.c | 9 ++ src/pc/djui/djui_panel_join.c | 9 ++ src/pc/djui/djui_panel_main.c | 20 +++- src/pc/lua/smlua_constants_autogen.c | 2 +- src/pc/mods/mods.c | 6 +- src/pc/network/version.c | 20 +++- src/pc/network/version.h | 3 +- src/pc/pc_main.c | 6 ++ src/pc/pc_main.h | 2 +- src/pc/update_checker.c | 143 ++++++++++++++++++++++++++ src/pc/update_checker.h | 11 ++ 15 files changed, 229 insertions(+), 18 deletions(-) create mode 100644 src/pc/update_checker.c create mode 100644 src/pc/update_checker.h diff --git a/Makefile b/Makefile index 9ccd2f59b..218561295 100644 --- a/Makefile +++ b/Makefile @@ -904,6 +904,13 @@ endif # Zlib LDFLAGS += -lz +# Update checker library +ifeq ($(WINDOWS_BUILD),1) + LDFLAGS += -lwininet +else + LDFLAGS += -lcurl +endif + # Lua ifeq ($(WINDOWS_BUILD),1) ifeq ($(TARGET_BITS), 32) @@ -927,7 +934,7 @@ else LDFLAGS += -Llib/lua/linux -l:liblua53.a -ldl endif -# coopnet +# CoopNet COOPNET_LIBS := ifeq ($(COOPNET),1) ifeq ($(WINDOWS_BUILD),1) diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index aaf2d26b1..63e4dad81 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -12605,7 +12605,7 @@ MINOR_VERSION_NUMBER = 1 PATCH_VERSION_NUMBER = 0 --- @type string -SM64COOPDX_VERSION = "v0.1.4" +SM64COOPDX_VERSION = "0.1.5" --- @type integer VERSION_NUMBER = 36 diff --git a/lang/English.ini b/lang/English.ini index df935bf5d..4a3104169 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -28,6 +28,9 @@ COOPNET_VERSION = "\\#ffa0a0\\Your version is no longer compatible with CoopNet. PEER_FAILED = "\\#ffa0a0\\Failed to connect to player '@'" UNKNOWN = "unknown" LOBBY_HOST = "the lobby's host" +UPDATE_AVAILABLE = "A new update is available!" +LATEST_VERSION = "Latest version" +YOUR_VERSION = "Your version" [CHAT] KICKING = "Kicking '@'!" diff --git a/src/pc/crash_handler.c b/src/pc/crash_handler.c index 124d3959c..250c74e6c 100644 --- a/src/pc/crash_handler.c +++ b/src/pc/crash_handler.c @@ -290,7 +290,7 @@ static void crash_handler_add_info_str(CrashHandlerText** pTextP, f32 x, f32 y, static void crash_handler_add_version_str(CrashHandlerText** pTextP, f32 x, f32 y) { CrashHandlerText* pText = *pTextP; - crash_handler_set_text(x, y, 0xFF, 0xFF, 0x00, "%s", "sm64coopdx "); + crash_handler_set_text(x, y, 0xFF, 0xFF, 0x00, "%s", "sm64coopdx v"); crash_handler_set_text(-1, y, 0x00, 0xFF, 0xFF, "%s", SM64COOPDX_VERSION); *pTextP = pText; } diff --git a/src/pc/djui/djui_panel_host.c b/src/pc/djui/djui_panel_host.c index fe2baf70e..173ea1df0 100644 --- a/src/pc/djui/djui_panel_host.c +++ b/src/pc/djui/djui_panel_host.c @@ -10,6 +10,7 @@ #include "pc/network/network.h" #include "pc/utils/misc.h" #include "pc/configfile.h" +#include "pc/update_checker.h" static struct DjuiRect* sRectPort = NULL; static struct DjuiRect* sRectPassword = NULL; @@ -201,6 +202,14 @@ void djui_panel_host_create(struct DjuiBase* caller) { ? &button1->base : &button2->base; } + + if (gUpdateMessage) { + struct DjuiText* message = djui_text_create(&panel->base, DLANG(NOTIF, UPDATE_AVAILABLE)); + djui_base_set_size_type(&message->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&message->base, 1.0f, 1.0f); + djui_base_set_color(&message->base, 255, 255, 160, 255); + djui_text_set_alignment(message, DJUI_HALIGN_CENTER, DJUI_VALIGN_BOTTOM); + } } djui_panel_add(caller, panel, defaultBase); diff --git a/src/pc/djui/djui_panel_join.c b/src/pc/djui/djui_panel_join.c index 56795a1fe..2d57ee7c6 100644 --- a/src/pc/djui/djui_panel_join.c +++ b/src/pc/djui/djui_panel_join.c @@ -6,6 +6,7 @@ #include "djui_panel_join_direct.h" #include "pc/network/network.h" #include "pc/utils/misc.h" +#include "pc/update_checker.h" #ifdef DISCORD_SDK #include "pc/discord/discord.h" #endif @@ -38,6 +39,14 @@ void djui_panel_join_create(struct DjuiBase* caller) { djui_button_create(body, DLANG(MENU, BACK), DJUI_BUTTON_STYLE_BACK, djui_panel_menu_back); } + if (gUpdateMessage) { + struct DjuiText* message = djui_text_create(&panel->base, DLANG(NOTIF, UPDATE_AVAILABLE)); + djui_base_set_size_type(&message->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&message->base, 1.0f, 1.0f); + djui_base_set_color(&message->base, 255, 255, 160, 255); + djui_text_set_alignment(message, DJUI_HALIGN_CENTER, DJUI_VALIGN_BOTTOM); + } + djui_panel_add(caller, panel, NULL); #endif } diff --git a/src/pc/djui/djui_panel_main.c b/src/pc/djui/djui_panel_main.c index b8b9d4bec..3c6e3696a 100644 --- a/src/pc/djui/djui_panel_main.c +++ b/src/pc/djui/djui_panel_main.c @@ -7,6 +7,7 @@ #include "djui_panel_confirm.h" #include "src/pc/controller/controller_sdl.h" #include "src/pc/pc_main.h" +#include "src/pc/update_checker.h" extern ALIGNED8 u8 texture_coopdx_logo[]; @@ -46,11 +47,20 @@ void djui_panel_main_create(struct DjuiBase* caller) { djui_base_set_location(&button4->base, 0, -30.0f); } - struct DjuiText* version = djui_text_create(&panel->base, SM64COOPDX_VERSION); - djui_base_set_size_type(&version->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); - djui_base_set_size(&version->base, 1.0f, 1.0f); - djui_base_set_color(&version->base, 50, 50, 50, 255); - djui_text_set_alignment(version, DJUI_HALIGN_RIGHT, DJUI_VALIGN_BOTTOM); + // these two cannot co-exist for some reason + if (gUpdateMessage) { + struct DjuiText* message = djui_text_create(&panel->base, DLANG(NOTIF, UPDATE_AVAILABLE)); + djui_base_set_size_type(&message->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&message->base, 1.0f, 1.0f); + djui_base_set_color(&message->base, 255, 255, 160, 255); + djui_text_set_alignment(message, DJUI_HALIGN_CENTER, DJUI_VALIGN_BOTTOM); + } else { + struct DjuiText* version = djui_text_create(&panel->base, get_version_dx()); + djui_base_set_size_type(&version->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&version->base, 1.0f, 1.0f); + djui_base_set_color(&version->base, 50, 50, 50, 255); + djui_text_set_alignment(version, DJUI_HALIGN_RIGHT, DJUI_VALIGN_BOTTOM); + } } djui_panel_add(caller, panel, NULL); diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index bd75e5598..9b2402607 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -4396,7 +4396,7 @@ char gSmluaConstants[] = "" "COOP_OBJ_FLAG_LUA = (1 << 1)\n" "COOP_OBJ_FLAG_NON_SYNC = (1 << 2)\n" "COOP_OBJ_FLAG_INITIALIZED = (1 << 3)\n" -"SM64COOPDX_VERSION = 'v0.1.4'\n" +"SM64COOPDX_VERSION = '0.1.5'\n" "VERSION_TEXT = 'beta'\n" "VERSION_NUMBER = 36\n" "MINOR_VERSION_NUMBER = 1\n" diff --git a/src/pc/mods/mods.c b/src/pc/mods/mods.c index 44575c4cb..b1bbd9f87 100644 --- a/src/pc/mods/mods.c +++ b/src/pc/mods/mods.c @@ -193,7 +193,7 @@ static u32 mods_count_directory(char* modsBasePath) { } static void mods_load(struct Mods* mods, char* modsBasePath, bool isUserModPath) { - if (gIsThreaded) { REFRESH_MUTEX(snprintf(gCurrLoadingSegment.str, 256, "Generating DynOS Packs in %s mod path:\n\\#808080\\%s", isUserModPath ? "user" : "local", modsBasePath)); } + if (gIsThreaded) { REFRESH_MUTEX(snprintf(gCurrLoadingSegment.str, 256, "Generating DynOS Packs In %s Mod Path:\n\\#808080\\%s", isUserModPath ? "User" : "Local", modsBasePath)); } // generate bins dynos_generate_packs(modsBasePath); @@ -223,7 +223,7 @@ static void mods_load(struct Mods* mods, char* modsBasePath, bool isUserModPath) } f32 count = (f32) mods_count_directory(modsBasePath); - if (gIsThreaded) { REFRESH_MUTEX(snprintf(gCurrLoadingSegment.str, 256, "Loading mods in %s mod path:\n\\#808080\\%s", isUserModPath ? "user" : "local", modsBasePath)); } + if (gIsThreaded) { REFRESH_MUTEX(snprintf(gCurrLoadingSegment.str, 256, "Loading Mods In %s Mod Path:\n\\#808080\\%s", isUserModPath ? "User" : "Local", modsBasePath)); } // iterate char path[SYS_MAX_PATH] = { 0 }; @@ -232,7 +232,7 @@ static void mods_load(struct Mods* mods, char* modsBasePath, bool isUserModPath) // sanity check / fill path[] if (!directory_sanity_check(dir, modsBasePath, path)) { continue; } - if (gIsThreaded) { REFRESH_MUTEX(snprintf(gCurrLoadingSegment.str, 256, "Loading mod:\n\\#808080\\%s/%s", modsBasePath, dir->d_name)); } + if (gIsThreaded) { REFRESH_MUTEX(snprintf(gCurrLoadingSegment.str, 256, "Loading Mod:\n\\#808080\\%s/%s", modsBasePath, dir->d_name)); } // load the mod if (!mod_load(mods, modsBasePath, dir->d_name)) { diff --git a/src/pc/network/version.c b/src/pc/network/version.c index 39029511f..d3fae4985 100644 --- a/src/pc/network/version.c +++ b/src/pc/network/version.c @@ -7,6 +7,7 @@ static char sVersionString[MAX_VERSION_LENGTH] = { 0 }; static char sLocalVersionString[MAX_LOCAL_VERSION_LENGTH] = { 0 }; +static char sDxVersionString[MAX_VERSION_LENGTH] = { 0 }; const char* get_version(void) { if (configCoopCompatibility) { @@ -25,9 +26,9 @@ const char* get_version(void) { #endif } else { #if defined(VERSION_US) - snprintf(sVersionString, MAX_VERSION_LENGTH, "%s", SM64COOPDX_VERSION); + snprintf(sVersionString, MAX_VERSION_LENGTH, "v%s", SM64COOPDX_VERSION); #else - snprintf(sVersionString, MAX_VERSION_LENGTH, "%s %s", SM64COOPDX_VERSION, VERSION_REGION); + snprintf(sVersionString, MAX_VERSION_LENGTH, "v%s %s", SM64COOPDX_VERSION, VERSION_REGION); #endif } return sVersionString; @@ -46,15 +47,26 @@ const char* get_version_local(void) { #endif } else { #if defined(VERSION_US) - snprintf(sLocalVersionString, MAX_LOCAL_VERSION_LENGTH, "%s", SM64COOPDX_VERSION); + snprintf(sLocalVersionString, MAX_LOCAL_VERSION_LENGTH, "v%s", SM64COOPDX_VERSION); #else - snprintf(sLocalVersionString, MAX_LOCAL_VERSION_LENGTH, "%s %s", VERSION_TEXT, SM64COOPDX_VERSION, VERSION_REGION); + snprintf(sLocalVersionString, MAX_LOCAL_VERSION_LENGTH, "v%s %s", VERSION_TEXT, SM64COOPDX_VERSION, VERSION_REGION); #endif } return sLocalVersionString; } +// I want to redo this whole file when I remove sm64ex-coop compatibility +const char* get_version_dx(void) { +#if defined(VERSION_US) + snprintf(sDxVersionString, MAX_LOCAL_VERSION_LENGTH, "v%s", SM64COOPDX_VERSION); +#else + snprintf(sDxVersionString, MAX_LOCAL_VERSION_LENGTH, "v%s %s", VERSION_TEXT, SM64COOPDX_VERSION, VERSION_REGION); +#endif + + return sDxVersionString; +} + const char* get_game_name(void) { if (configCoopCompatibility) { #ifdef DEVELOPMENT diff --git a/src/pc/network/version.h b/src/pc/network/version.h index 403a3b155..7d833fddb 100644 --- a/src/pc/network/version.h +++ b/src/pc/network/version.h @@ -1,7 +1,7 @@ #ifndef VERSION_H #define VERSION_H -#define SM64COOPDX_VERSION "v0.1.4" +#define SM64COOPDX_VERSION "0.1.5" #define VERSION_TEXT "beta" #define VERSION_NUMBER 36 @@ -22,6 +22,7 @@ #define MAX_LOCAL_VERSION_LENGTH 36 const char* get_version(void); const char* get_version_local(void); +const char* get_version_dx(void); const char* get_game_name(void); #endif diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index fd7992bfe..e479a0863 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -39,6 +39,7 @@ #include "pc/network/version.h" #include "pc/network/socket/domain_res.h" #include "pc/network/network_player.h" +#include "pc/update_checker.h" #include "pc/djui/djui.h" #include "pc/djui/djui_unicode.h" #include "pc/djui/djui_panel.h" @@ -278,6 +279,9 @@ void* main_game_init(UNUSED void* arg) { configfile_load(); configWindow.settings_changed = true; if (!djui_language_init(configLanguage)) { snprintf(configLanguage, MAX_CONFIG_STRING, "%s", ""); } + + check_for_updates(); + dynos_packs_init(); sync_objects_init_system(); @@ -369,6 +373,8 @@ int main(int argc, char *argv[]) { djui_init_late(); djui_console_message_dequeue(); + show_update_popup(); + // Init network if (gCLIOpts.Network == NT_CLIENT) { network_set_system(NS_SOCKET); diff --git a/src/pc/pc_main.h b/src/pc/pc_main.h index b99ab6f8d..f973b9bb7 100644 --- a/src/pc/pc_main.h +++ b/src/pc/pc_main.h @@ -60,7 +60,7 @@ extern "C" { #ifdef GIT_HASH #define TITLE ({ char title[96] = ""; snprintf(title, 96, "sm64coopdx [%s]", GIT_HASH); title; }) #else -#define TITLE ({ char title[96] = ""; snprintf(title, 96, "sm64coopdx %s", SM64COOPDX_VERSION); title; }) +#define TITLE ({ char title[96] = ""; snprintf(title, 96, "sm64coopdx v%s", SM64COOPDX_VERSION); title; }) #endif #define AT_STARTUP __attribute__((constructor)) diff --git a/src/pc/update_checker.c b/src/pc/update_checker.c new file mode 100644 index 000000000..99108c63b --- /dev/null +++ b/src/pc/update_checker.c @@ -0,0 +1,143 @@ +#include +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#else +#include +#endif + +#include "update_checker.h" +#include "pc/djui/djui.h" +#include "pc/network/version.h" +#include "pc/loading.h" + +#define URL "https://sm64coopdx.com/download/version.txt" + +static char sVersionUpdateTextBuffer[256] = { 0 }; +static char sRemoteVersion[8]; + +bool gUpdateMessage = false; + +void show_update_popup(void) { + if (sVersionUpdateTextBuffer[0] == '\0') { return; } + djui_popup_create(sVersionUpdateTextBuffer, 3); +} + +#if !(defined(_WIN32) || defined(_WIN64)) +size_t write_callback(char *ptr, size_t size, size_t nmemb, char **data) { + size_t realsize = size * nmemb; + + // allocate memory for the received data and copy it into the buffer + *data = realloc(*data, realsize + 1); + if (*data == NULL) { return 0; } + + memcpy(*data, ptr, realsize); + (*data)[realsize] = '\0'; // null-terminate the string + + return realsize; +} +#endif + +// function to download a text file from the internet +const char* get_version_remote(void) { +#if defined(_WIN32) || defined(_WIN64) + HINTERNET hInternet, hUrl; + DWORD bytesRead; + char buffer[8]; + + // initialize WinINet + hInternet = InternetOpenA("sm64coopdx", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + if (!hInternet) { + printf("Failed to check for updates!\n"); + InternetCloseHandle(hInternet); + InternetCloseHandle(hUrl); + return NULL; + } + + // open the URL + hUrl = InternetOpenUrlA(hInternet, URL, NULL, 0, INTERNET_FLAG_RELOAD, 0); + if (!hUrl) { + printf("Failed to check for updates!\n"); + InternetCloseHandle(hInternet); + InternetCloseHandle(hUrl); + return NULL; + } + + // calculate the size of the file + DWORD contentLength; + DWORD dwSize = sizeof(contentLength); + HttpQueryInfo(hUrl, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &contentLength, &dwSize, NULL); + + // read data from the URL + if (!InternetReadFile(hUrl, buffer, sizeof(buffer), &bytesRead)) { + printf("Failed to check for updates!\n"); + InternetCloseHandle(hInternet); + InternetCloseHandle(hUrl); + return NULL; + } + + strncpy(sRemoteVersion, buffer, 8); + + // close handles + InternetCloseHandle(hUrl); + InternetCloseHandle(hInternet); +#else + CURL *curl; + CURLcode res; + char *data = NULL; + + // initialize libcurl + curl = curl_easy_init(); + if (!curl) { + printf("Failed to check for updates!\n"); + return NULL; + } + + // set the URL to retrieve data from + curl_easy_setopt(curl, CURLOPT_URL, URL); + + // set the write callback function to store received data into the buffer + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + + // perform the request + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + printf("Failed to check for updates!\n"); + curl_easy_cleanup(curl); + return NULL; + } + + strncpy(sRemoteVersion, data, 8); + + // Clean up + curl_easy_cleanup(curl); +#endif + + return sRemoteVersion; +} + +void check_for_updates(void) { + REFRESH_MUTEX( + gCurrLoadingSegment.percentage = 0; + snprintf(gCurrLoadingSegment.str, 256, "Checking For Updates"); + ); + +#if defined(_WIN32) || defined(_WIN64) + const char* remoteVersion = get_version_remote(); + if (strcmp(get_version_remote(), SM64COOPDX_VERSION)) { + snprintf( + sVersionUpdateTextBuffer, 256, + "\\#ffffa0\\%s\n\\#dcdcdc\\%s: v%s\n%s: v%s", + DLANG(NOTIF, UPDATE_AVAILABLE), + DLANG(NOTIF, LATEST_VERSION), + remoteVersion, + DLANG(NOTIF, YOUR_VERSION), + SM64COOPDX_VERSION + ); + gUpdateMessage = true; + } +#else + +#endif +} diff --git a/src/pc/update_checker.h b/src/pc/update_checker.h new file mode 100644 index 000000000..0454ebb21 --- /dev/null +++ b/src/pc/update_checker.h @@ -0,0 +1,11 @@ +#ifndef _UPDATE_CHECKER_H +#define _UPDATE_CHECKER_H + +#include + +extern bool gUpdateMessage; + +void show_update_popup(void); +void check_for_updates(void); + +#endif \ No newline at end of file