From 23bafb943f6fafd97d45d488c3fd276583d89770 Mon Sep 17 00:00:00 2001 From: Phireh Date: Mon, 16 Dec 2024 21:40:04 +0100 Subject: [PATCH] Functional hot reloading --- Makefile | 8 ++++---- game.c | 2 +- game.h | 1 + wayland.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 220194e..bd74c15 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ INTERFACEDIR:=$(shell pkg-config --variable=pkgdatadir wayland-protocols) -CFLAGS=-Wall -Wextra -Og -ggdb3 -Wno-unused-but-set-variable -Wno-unused-parameter +CFLAGS=-Wall -Wextra -Og -ggdb3 -Wno-unused-but-set-variable -Wno-unused-parameter -Wl,--export-dynamic LIBS=-lwayland-client -lwayland-egl -lEGL -lOpenGL -lxkbcommon -levdev -wayland: wayland.c xdg-shell-protocol.c xdg-shell-client-protocol.h zwp-text-input-unstable-v3-client-protocol.c zwp-text-input-unstable-v3-protocol.h xdg-decoration-unstable-v1.c xdg-decoration-unstable-v1.h - gcc $(CFLAGS) $(LIBS) wayland.c xdg-shell-protocol.c xdg-decoration-unstable-v1.c zwp-text-input-unstable-v3-client-protocol.c -o wayland +wayland: wayland.c xdg-shell-protocol.c xdg-shell-client-protocol.h zwp-text-input-unstable-v3-client-protocol.c zwp-text-input-unstable-v3-protocol.h xdg-decoration-unstable-v1.c xdg-decoration-unstable-v1.h game.h + gcc $(CFLAGS) $(LIBS) $^ -o wayland # TODO: Normalize these annoying names xdg-shell-protocol.c: @@ -18,4 +18,4 @@ xdg-decoration-unstable-v1.c: xdg-decoration-unstable-v1.h: wayland-scanner client-header $(INTERFACEDIR)/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml xdg-decoration-unstable-v1.h game.so: game.c game.h - gcc $(CFLAGS) game.c --shared -o game.so + gcc $(CFLAGS) game.c -shared -o game.so diff --git a/game.c b/game.c index e5b1da3..772b184 100644 --- a/game.c +++ b/game.c @@ -3,5 +3,5 @@ void simulate_frame(game_state_t *state) { // TODO: Do something amazing here - ; + state->version = 1; } diff --git a/game.h b/game.h index 25bba70..0b1d368 100644 --- a/game.h +++ b/game.h @@ -11,6 +11,7 @@ typedef struct { int32_t window_w; int64_t frame; double dt; + int32_t version; } game_state_t; #endif diff --git a/wayland.c b/wayland.c index 093c898..664c00d 100644 --- a/wayland.c +++ b/wayland.c @@ -18,6 +18,7 @@ #include #include #include +#include // libevdev #include // generated header using wayland-scanner @@ -70,12 +71,14 @@ error_level_t error_verbosity = { ERROR_LEVEL_DEBUG }; typedef void (*simulate_frame_fn)(game_state_t*); -/* Game layer */ +/* Game layer and hot reload */ typedef struct { simulate_frame_fn simulate_frame; } game_api_t; game_api_t game_api = {}; +time_t hr_timestamp = 0; +void *dlhandle = NULL; char *egl_s(int code) { @@ -742,16 +745,25 @@ int main() wl_surface_commit(surface); // Trying to link with game layer - void *dlhandle = dlopen("./game.so", RTLD_NOW); + dlhandle = dlopen("./game.so", RTLD_NOW | RTLD_LOCAL); + if (!dlhandle) + { console_err_ss(MODULE_HOTRELOAD, "Could not load game layer\n"); - dlerror(); + dlerror(); + } - game_api.simulate_frame = (simulate_frame_fn)dlsym(dlhandle, "simulate_frame"); + struct stat fs; + if (stat("./game.so", &fs) == -1) + console_err_ss(MODULE_HOTRELOAD, "Could not get info for game library: %s\n", strerror(errno)); - char *dlerr = NULL; - if ((dlerr = dlerror())) - console_err_ss(MODULE_HOTRELOAD, "Could not link game layer: %s\n", dlerr); + { + hr_timestamp = fs.st_mtime; + game_api.simulate_frame = (simulate_frame_fn)dlsym(dlhandle, "simulate_frame"); + char *dlerr = NULL; + if ((dlerr = dlerror())) + console_err_ss(MODULE_HOTRELOAD, "Could not link game layer: %s\n", dlerr); + } // Main event loop int framecount = 0; @@ -759,6 +771,37 @@ int main() { wl_display_dispatch_pending(display); + // TODO: Do not try to hot-reload every frame. Lmao + struct stat file_stats; + if (stat("./game.so", &file_stats) == -1) + { + console_err_ss(MODULE_HOTRELOAD, "Could not get info for game library: %s\n", strerror(errno)); + } + else if (hr_timestamp < file_stats.st_mtime) + { + console_log_ss(MODULE_HOTRELOAD, "Game library has changed: trying to hot reload\n"); + + // NOTE: We cannot load new symbols from the same path unless we close the currently open handle. + // However, until the linker is done with the file it will fail on dlopen. + // For now we just busywait trying to link it. + dlclose(dlhandle); + while (!(dlhandle = dlopen("./game.so", RTLD_NOW | RTLD_LOCAL))); + + + hr_timestamp = fs.st_mtime; + game_api.simulate_frame = (simulate_frame_fn)dlsym(dlhandle, "simulate_frame"); + char *dlerr = NULL; + if ((dlerr = dlerror())) + console_err_ss(MODULE_HOTRELOAD, "Could not link game layer: %s\n", dlerr); + + hr_timestamp = file_stats.st_mtime; + char *error_string = dlerror(); + if (error_string) + console_err_ss(MODULE_HOTRELOAD, "Error loading game code: %s\n", error_string); + + console_log_ss(MODULE_HOTRELOAD, "Successfully hot reloaded game layer\n"); + } + /* Controller input handling */ if (libevdev_get_fd(libevdev) != -1) // check if we actually found a controller to query { @@ -848,8 +891,9 @@ int main() game_state.frame = framecount; // TODO: Get delta time since last frame game_state.dt = 0; - game_api.simulate_frame(&game_state); + if (game_state.version != 1) + console_debug_ss(MODULE_HOTRELOAD, "Version %d\n", game_state.version); //console_debug_ss(MODULE_WAYLAND, "Frame %d\n", framecount); glClearColor(0.5, 0.3, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT);