From 3cd5b0bcca694630fcb4b977ddf7be7cd1bce153 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 20 Jan 2024 15:54:54 -0800 Subject: Rename gltfview -> game. --- game/src/game.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 game/src/game.c (limited to 'game/src/game.c') diff --git a/game/src/game.c b/game/src/game.c new file mode 100644 index 0000000..64be4f3 --- /dev/null +++ b/game/src/game.c @@ -0,0 +1,216 @@ +/* + * Main game module with entry point and game loop. + * + * The game module sets up the window and GL context and defers the core game + * logic to a plugin. + */ +#define _GNU_SOURCE 200112L // For readlink() + +#include "game.h" + +#include "plugins/plugin.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#undef _GNU_SOURCE + +static const int WIDTH = 1350; +static const int HEIGHT = 900; +static const int MAX_FPS = 60; + +/// Initialize the game's plugin. +static bool init_plugin(Game* game) { + assert(game); + assert(game->plugin); + // Plugin state is allowed to be null, either when the plugin does not + // expose an init() or when init() does not initialize a state. + if (plugin_resolve(game->plugin, plugin_init, "init")) { + State* plugin_state = 0; + if (!plugin_call(game->plugin, plugin_init, "init", game, &plugin_state)) { + return false; + } + set_plugin_state(game->plugin, plugin_state); + } + return true; // Plugin does not need to expose an init(). +} + +/// Shutdown the game's plugin. +/// The game's plugin is allowed to be null in the call to this function. +static void shutdown_plugin(Game* game) { + assert(game); + if (game->plugin && + (plugin_resolve(game->plugin, plugin_shutdown, "shutdown"))) { + void* plugin_state = get_plugin_state(game->plugin); + plugin_call(game->plugin, plugin_shutdown, "shutdown", game, plugin_state); + set_plugin_state(game->plugin, 0); + } +} + +/// Boot the game's plugin. +static bool boot_plugin(Game* game) { + assert(game); + assert(game->plugin); + if (plugin_resolve(game->plugin, plugin_boot, "boot")) { + void* plugin_state = get_plugin_state(game->plugin); + return plugin_call(game->plugin, plugin_boot, "boot", game, plugin_state); + } + return true; // Plugin does not need to expose a boot(). +} + +/// Update the plugin's state. +static void update_plugin(Game* game, double t, double dt) { + assert(game); + assert(game->plugin); + if (plugin_resolve(game->plugin, plugin_update, "update")) { + void* plugin_state = get_plugin_state(game->plugin); + plugin_call( + game->plugin, plugin_update, "update", game, plugin_state, t, dt); + } +} + +/// Plugin render. +static void render_plugin(const Game* game) { + assert(game); + assert(game->plugin); + if (plugin_resolve(game->plugin, plugin_render, "render")) { + void* plugin_state = get_plugin_state(game->plugin); + plugin_call(game->plugin, plugin_render, "render", game, plugin_state); + } +} + +/// Plugin resize. +static void resize_plugin(Game* game, int width, int height) { + assert(game); + assert(game->plugin); + if (plugin_resolve(game->plugin, plugin_resize, "resize")) { + void* plugin_state = get_plugin_state(game->plugin); + plugin_call( + game->plugin, plugin_resize, "resize", game, plugin_state, width, + height); + } +} + +void app_end(Game* game); + +bool app_init(const GfxAppDesc* desc, void** app_state) { + assert(desc); + + if (desc->argc <= 1) { + LOGE("Usage: %s [plugin args]", desc->argv[0]); + return false; + } + + Game* game = calloc(1, sizeof(Game)); + if (!game) { + LOGE("Failed to allocate game state"); + return false; + } + + // Syntax: game [plugin args] + // + // Here we consume the arg so that plugins receive the remainder + // args starting from 0. + game->argc = desc->argc - 1; + game->argv = desc->argv + 1; + + char exe_path_buf[NAME_MAX] = {0}; + if (readlink("/proc/self/exe", exe_path_buf, sizeof(exe_path_buf)) == -1) { + LOGE("readlink(/proc/self/exe) failed"); + goto cleanup; + } + + // Replace the last / with a null terminator to remove the exe file from the + // path. This gets the file's parent directory. + *strrchr(exe_path_buf, '/') = 0; + + const mstring exe_dir = mstring_make(exe_path_buf); + const mstring plugins_path = mstring_concat_cstr(exe_dir, "/src/plugins"); + + if (!(game->plugin_engine = new_plugin_engine( + &(PluginEngineDesc){.plugins_dir = mstring_cstr(&plugins_path)}))) { + goto cleanup; + } + + const char* plugin = desc->argv[1]; + if (!(game->plugin = load_plugin(game->plugin_engine, plugin))) { + goto cleanup; + } + + if (!(game->gfx = gfx_init())) { + goto cleanup; + } + + if (!init_plugin(game)) { + goto cleanup; + } + if (!boot_plugin(game)) { + goto cleanup; + } + + *app_state = game; + return true; + +cleanup: + LOGE("Gfx error: %s", get_error()); + app_end(game); + return false; +} + +void app_end(Game* game) { + assert(game); + shutdown_plugin(game); + if (game->gfx) { + gfx_destroy(&game->gfx); + } + if (game->plugin) { + delete_plugin(&game->plugin); + } + if (game->plugin_engine) { + delete_plugin_engine(&game->plugin_engine); + } +} + +void app_update(Game* game, double t, double dt) { + plugin_engine_update(game->plugin_engine); + if (plugin_reloaded(game->plugin)) { + shutdown_plugin(game); + const bool result = init_plugin(game); + assert(result); // TODO: handle error better. + } + + update_plugin(game, t, dt); +} + +void app_render(const Game* game) { + RenderBackend* render_backend = gfx_get_render_backend(game->gfx); + gfx_start_frame(render_backend); + render_plugin(game); + gfx_end_frame(render_backend); +} + +void app_resize(Game* game, int width, int height) { + resize_plugin(game, width, height); +} + +GFX_APP_MAIN(WIDTH, HEIGHT, MAX_FPS); -- cgit v1.2.3