161 lines
3.8 KiB
C
161 lines
3.8 KiB
C
#include "twn_game_object_c.h"
|
|
#include "twn_engine_context_c.h"
|
|
#include "twn_util_c.h"
|
|
|
|
#include <x-watcher.h>
|
|
#include <SDL2/SDL.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
#define GAME_OBJECT_NAME "libgame.so"
|
|
#define MODIFIED_TICKS_MERGED 10
|
|
|
|
|
|
static void (*game_tick_callback)(void);
|
|
static void (*game_end_callback)(void);
|
|
|
|
static x_watcher *watcher;
|
|
static void *handle = NULL;
|
|
|
|
static uint64_t last_tick_modified;
|
|
static bool loaded_after_modification = true;
|
|
static SDL_mutex *lock;
|
|
|
|
static char *game_object_path;
|
|
|
|
static void load_game_object(void) {
|
|
/* needs to be closed otherwise symbols aren't resolved again */
|
|
if (handle) {
|
|
dlclose(handle);
|
|
handle = NULL;
|
|
game_tick_callback = NULL;
|
|
game_end_callback = NULL;
|
|
}
|
|
|
|
void *new_handle = dlopen(game_object_path, RTLD_LAZY);
|
|
if (!new_handle) {
|
|
log_critical("Hot Reload Error: Cannot open game code shared object (%s)", dlerror());
|
|
goto ERR_OPENING_SO;
|
|
}
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
|
|
game_tick_callback = (void (*)(void))dlsym(new_handle, "game_tick");
|
|
if (!game_tick_callback) {
|
|
CRY("Hot Reload Error", "game_tick_callback() symbol wasn't found");
|
|
goto ERR_GETTING_PROC;
|
|
}
|
|
|
|
game_end_callback = (void (*)(void))dlsym(new_handle, "game_end");
|
|
if (!game_end_callback) {
|
|
CRY("Hot Reload Error", "game_end_callback() symbol wasn't found");
|
|
goto ERR_GETTING_PROC;
|
|
}
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
handle = new_handle;
|
|
|
|
if (ctx.game.frame_number != 0)
|
|
log_info("Game object was reloaded\n");
|
|
|
|
return;
|
|
|
|
ERR_GETTING_PROC:
|
|
dlclose(new_handle);
|
|
game_tick_callback = NULL;
|
|
game_end_callback = NULL;
|
|
|
|
ERR_OPENING_SO:
|
|
SDL_UnlockMutex(lock);
|
|
|
|
}
|
|
|
|
static void watcher_callback(XWATCHER_FILE_EVENT event,
|
|
const char *path,
|
|
int context,
|
|
void *data)
|
|
{
|
|
(void)context;
|
|
(void)path;
|
|
(void)data;
|
|
|
|
switch(event) {
|
|
case XWATCHER_FILE_CREATED:
|
|
case XWATCHER_FILE_MODIFIED:
|
|
SDL_LockMutex(lock);
|
|
last_tick_modified = ctx.game.frame_number;
|
|
loaded_after_modification = false;
|
|
SDL_UnlockMutex(lock);
|
|
break;
|
|
|
|
case XWATCHER_FILE_UNSPECIFIED:
|
|
case XWATCHER_FILE_REMOVED:
|
|
case XWATCHER_FILE_OPENED:
|
|
case XWATCHER_FILE_ATTRIBUTES_CHANGED:
|
|
case XWATCHER_FILE_NONE:
|
|
case XWATCHER_FILE_RENAMED:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void game_object_load(void) {
|
|
SDL_asprintf(&game_object_path, "%s%s", ctx.base_dir, GAME_OBJECT_NAME);
|
|
|
|
watcher = xWatcher_create();
|
|
|
|
xWatcher_reference dir;
|
|
dir.path = game_object_path;
|
|
dir.callback_func = watcher_callback;
|
|
|
|
xWatcher_appendFile(watcher, &dir);
|
|
xWatcher_start(watcher);
|
|
|
|
lock = SDL_CreateMutex();
|
|
load_game_object();
|
|
}
|
|
|
|
|
|
void game_object_unload(void) {
|
|
game_end_callback();
|
|
xWatcher_destroy(watcher);
|
|
watcher = NULL;
|
|
dlclose(handle);
|
|
handle = NULL;
|
|
game_tick_callback = NULL;
|
|
game_end_callback = NULL;
|
|
SDL_DestroyMutex(lock);
|
|
}
|
|
|
|
|
|
bool game_object_try_reloading(void) {
|
|
bool result = false;
|
|
|
|
/* only load the modified library after some time, as compilers make a lot of modifications */
|
|
SDL_LockMutex(lock);
|
|
if (ctx.game.frame_number - last_tick_modified > MODIFIED_TICKS_MERGED &&
|
|
!loaded_after_modification) {
|
|
load_game_object();
|
|
loaded_after_modification = true;
|
|
result = true;
|
|
} SDL_UnlockMutex(lock);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void game_object_tick(void) {
|
|
game_tick_callback();
|
|
}
|
|
|
|
void *game_object_get_game_tick_address(void) {
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
return (void *)&game_tick_callback;
|
|
#pragma GCC diagnostic pop
|
|
}
|