diff --git a/.gitignore b/.gitignore index c42e9ef03..12e547ad6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ # Executables *.exe +!updater/*/*.exe *.out *.app *.hex diff --git a/Makefile b/Makefile index 6a5de2ca8..4bf5eae9b 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,8 @@ ENHANCE_LEVEL_TEXTURES ?= 1 DISCORD_SDK ?= 1 # Enable CoopNet SDK (used for CoopNet server hosting) COOPNET ?= 1 +# Enable Updater (used for automatic updates) +UPDATER ?= 1 # Enable docker build workarounds DOCKERBUILD ?= 0 # Sets your optimization level for building. @@ -924,6 +926,22 @@ else endif endif +# Updater +UPDATER_EXEC := +ifeq ($(UPDATER),1) + ifeq ($(WINDOWS_BUILD),1) + UPDATER_EXEC += ./updater/win64/coopdx-updater.exe + else ifeq ($(OSX_BUILD),1) + ifeq ($(shell uname -m),arm64) + UPDATER_EXEC += ./updater/mac_arm/coopdx-updater + else + UPDATER_EXEC += ./updater/mac_intel/coopdx-updater + endif + else ifeq ($(TARGET_RPI),0) + UPDATER_EXEC += ./updater/linux/coopdx-updater + endif +endif + IS_DEV_OR_DEBUG := $(or $(filter 1,$(DEVELOPMENT)),$(filter 1,$(DEBUG)),0) ifeq ($(IS_DEV_OR_DEBUG),0) CFLAGS += -fno-ident -fno-common -ffile-prefix-map="$(PWD)"=. -D__DATE__="\"\"" -D__TIME__="\"\"" -Wno-builtin-macro-redefined @@ -1126,6 +1144,9 @@ $(BUILD_DIR)/$(DISCORD_SDK_LIBS): $(BUILD_DIR)/$(COOPNET_LIBS): @$(CP) -f $(COOPNET_LIBS) $(BUILD_DIR) +$(BUILD_DIR)/$(UPDATER_EXEC): + @$(CP) -f $(UPDATER_EXEC) $(BUILD_DIR) + $(BUILD_DIR)/$(LANG_DIR): @$(CP) -f -r $(LANG_DIR) $(BUILD_DIR) @@ -1474,7 +1495,7 @@ ifeq ($(TARGET_N64),1) $(BUILD_DIR)/$(TARGET).objdump: $(ELF) $(OBJDUMP) -D $< > $@ else - $(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(BUILD_DIR)/$(RPC_LIBS) $(BUILD_DIR)/$(DISCORD_SDK_LIBS) $(BUILD_DIR)/$(COOPNET_LIBS) $(BUILD_DIR)/$(LANG_DIR) $(BUILD_DIR)/$(MOD_DIR) $(BUILD_DIR)/$(PALETTES_DIR) + $(EXE): $(O_FILES) $(MIO0_FILES:.mio0=.o) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(BUILD_DIR)/$(RPC_LIBS) $(BUILD_DIR)/$(DISCORD_SDK_LIBS) $(BUILD_DIR)/$(COOPNET_LIBS) $(BUILD_DIR)/$(UPDATER_EXEC) $(BUILD_DIR)/$(LANG_DIR) $(BUILD_DIR)/$(MOD_DIR) $(BUILD_DIR)/$(PALETTES_DIR) @$(PRINT) "$(GREEN)Linking executable: $(BLUE)$@ $(NO_COL)\n" $(V)$(LD) $(PROF_FLAGS) -L $(BUILD_DIR) -o $@ $(O_FILES) $(ULTRA_O_FILES) $(GODDARD_O_FILES) $(LDFLAGS) endif @@ -1512,6 +1533,7 @@ all: cp build/us_pc/discord_game_sdk.dylib $(APP_MACOS_DIR); \ cp build/us_pc/libdiscord_game_sdk.dylib $(APP_MACOS_DIR); \ cp build/us_pc/libcoopnet.dylib $(APP_MACOS_DIR); \ + cp build/us_pc/coopdx-updater $(APP_MACOS_DIR); \ cp build/us_pc/libjuice.1.6.2.dylib $(APP_MACOS_DIR); \ cp $(SDL2_LIB) $(APP_MACOS_DIR)/libSDL2.dylib; \ install_name_tool -change $(BREW_PREFIX)/lib/libSDL2-2.0.0.dylib @executable_path/libSDL2.dylib $(APP_MACOS_DIR)/sm64coopdx > /dev/null 2>&1; \ diff --git a/src/pc/djui/djui.c b/src/pc/djui/djui.c index 43218a151..07bb37986 100644 --- a/src/pc/djui/djui.c +++ b/src/pc/djui/djui.c @@ -5,6 +5,7 @@ #include "djui_panel_pause.h" #include "djui_panel_join.h" #include "djui_panel_join_message.h" +#include "djui_panel_confirm.h" #include "djui_ctx_display.h" #include "djui_fps_display.h" #include "djui_lua_profiler.h" @@ -152,6 +153,17 @@ void djui_connect_menu_open(void) { djui_panel_join_message_create(NULL); } +static void djui_update_game(UNUSED struct DjuiBase *caller) { + update_game(); +} + +void djui_open_update_panel(void) { + djui_panel_shutdown(); + gDjuiInMainMenu = true; + djui_panel_main_create(NULL); + djui_panel_confirm_create(NULL, "UPDATE", "An update is available. Would you like to install it?", djui_update_game); +} + void djui_lua_error(char* text, struct DjuiColor color) { if (!sDjuiLuaError) { return; } djui_base_set_color(&sDjuiLuaError->base, color.r, color.g, color.b, color.a); diff --git a/src/pc/djui/djui.h b/src/pc/djui/djui.h index da793bb23..cf5b16c22 100644 --- a/src/pc/djui/djui.h +++ b/src/pc/djui/djui.h @@ -46,6 +46,7 @@ extern bool gDjuiDisabled; void djui_init(void); void djui_init_late(void); void djui_connect_menu_open(void); +void djui_open_update_panel(void); void djui_lua_error(char* text, struct DjuiColor color); void djui_lua_error_clear(void); void djui_render(void); diff --git a/src/pc/pc_main.c b/src/pc/pc_main.c index 722545822..d4a780097 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.c @@ -577,6 +577,10 @@ int main(int argc, char *argv[]) { show_update_popup(); + if (can_update_game()) { + djui_open_update_panel(); + } + // initialize network if (gCLIOpts.network == NT_CLIENT) { network_set_system(NS_SOCKET); diff --git a/src/pc/utils/misc.c b/src/pc/utils/misc.c index 13f89fccd..0eec8bbe6 100644 --- a/src/pc/utils/misc.c +++ b/src/pc/utils/misc.c @@ -1,3 +1,9 @@ +#ifdef _WIN32 +#include +#else +#include +#endif + #include #include #include @@ -14,6 +20,7 @@ #include "engine/math_util.h" #include "pc/configfile.h" #include "pc/pc_main.h" +#include "pc/update_checker.h" float smooth_step(float edge0, float edge1, float x) { float t = (x - edge0) / (edge1 - edge0); @@ -594,3 +601,47 @@ void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, } } } + +static char *get_update_path(void) { +#ifdef _WIN32 + char updateExecFilename[] = "coopdx-updater.exe"; +#else + char updateExecFilename[] = "coopdx-updater"; +#endif + static char sUpdateExecFilePath[SYS_MAX_PATH]; + // this may truncate as sys_exe_path_dir is allocated to be of size SYS_MAX_SIZE, nonetheless such a limit should not be hit during normal use. + snprintf(sUpdateExecFilePath, sizeof(sUpdateExecFilePath), "%s%s%s", sys_exe_path_dir(), PATH_SEPARATOR, updateExecFilename); + return sUpdateExecFilePath; +} + +bool can_update_game(void) { + // the file is not guaranteed to exist, so make sure we have the updater installed + return fs_sys_file_exists(get_update_path()) && gUpdateMessage; +} + +void update_game(void) { + const char *updateExecFilePath = get_update_path(); + +#ifdef _WIN32 + STARTUPINFOA si = { 0 }; + PROCESS_INFORMATION pi = { 0 }; + + si.cb = sizeof(si); + + char commandBuf[SYS_MAX_PATH]; + // this can truncate, but under normal use, SYS_MAX_PATH should not ever be filled up + snprintf(commandBuf, sizeof(commandBuf), "%s --game-update", updateExecFilePath); + + if (CreateProcessA(NULL, commandBuf, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + exit(0); +#else + fclose(stdin); + fclose(stdout); + fclose(stderr); + execl(updateExecFilePath, "coopdx-updater", "--game-update", NULL); + exit(1); +#endif +} \ No newline at end of file diff --git a/src/pc/utils/misc.h b/src/pc/utils/misc.h index 01d813fa1..f18362717 100644 --- a/src/pc/utils/misc.h +++ b/src/pc/utils/misc.h @@ -38,4 +38,7 @@ void detect_and_skip_mtx_interpolation(Mtx** mtxPrev, Mtx** mtx); void str_seperator_concat(char *output_buffer, int buffer_size, char** strings, int num_strings, char* seperator); +bool can_update_game(void); +void update_game(void); + #endif \ No newline at end of file diff --git a/updater/linux/coopdx-updater b/updater/linux/coopdx-updater new file mode 100755 index 000000000..08ad3505f Binary files /dev/null and b/updater/linux/coopdx-updater differ diff --git a/updater/mac_arm/coopdx-updater b/updater/mac_arm/coopdx-updater new file mode 100755 index 000000000..1ee9bbbee Binary files /dev/null and b/updater/mac_arm/coopdx-updater differ diff --git a/updater/mac_intel/coopdx-updater b/updater/mac_intel/coopdx-updater new file mode 100755 index 000000000..c63dde53b Binary files /dev/null and b/updater/mac_intel/coopdx-updater differ diff --git a/updater/win64/coopdx-updater.exe b/updater/win64/coopdx-updater.exe new file mode 100755 index 000000000..48f15255c Binary files /dev/null and b/updater/win64/coopdx-updater.exe differ