summaryrefslogtreecommitdiff
path: root/game/src/game.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-01-20 15:54:54 -0800
committer3gg <3gg@shellblade.net>2024-01-20 15:54:54 -0800
commit3cd5b0bcca694630fcb4b977ddf7be7cd1bce153 (patch)
treee1a0e02ab5471d0f2ffb499f18109d6790241798 /game/src/game.c
parent02ec7cd07213e267fda7e1e67b62f55e92a2f32c (diff)
Rename gltfview -> game.
Diffstat (limited to 'game/src/game.c')
-rw-r--r--game/src/game.c216
1 files changed, 216 insertions, 0 deletions
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 @@
1/*
2 * Main game module with entry point and game loop.
3 *
4 * The game module sets up the window and GL context and defers the core game
5 * logic to a plugin.
6 */
7#define _GNU_SOURCE 200112L // For readlink()
8
9#include "game.h"
10
11#include "plugins/plugin.h"
12
13#include <gfx/gfx.h>
14#include <gfx/gfx_app.h>
15#include <gfx/render_backend.h>
16#include <gfx/renderer.h>
17#include <gfx/scene/camera.h>
18#include <gfx/scene/node.h>
19#include <gfx/scene/object.h>
20#include <gfx/scene/scene.h>
21
22#include <error.h>
23#include <log/log.h>
24#include <math/camera.h>
25#include <plugin.h>
26
27#include <assert.h>
28#include <stdbool.h>
29#include <stdio.h>
30#include <stdlib.h>
31
32#include <linux/limits.h>
33
34#include <unistd.h>
35
36#undef _GNU_SOURCE
37
38static const int WIDTH = 1350;
39static const int HEIGHT = 900;
40static const int MAX_FPS = 60;
41
42/// Initialize the game's plugin.
43static bool init_plugin(Game* game) {
44 assert(game);
45 assert(game->plugin);
46 // Plugin state is allowed to be null, either when the plugin does not
47 // expose an init() or when init() does not initialize a state.
48 if (plugin_resolve(game->plugin, plugin_init, "init")) {
49 State* plugin_state = 0;
50 if (!plugin_call(game->plugin, plugin_init, "init", game, &plugin_state)) {
51 return false;
52 }
53 set_plugin_state(game->plugin, plugin_state);
54 }
55 return true; // Plugin does not need to expose an init().
56}
57
58/// Shutdown the game's plugin.
59/// The game's plugin is allowed to be null in the call to this function.
60static void shutdown_plugin(Game* game) {
61 assert(game);
62 if (game->plugin &&
63 (plugin_resolve(game->plugin, plugin_shutdown, "shutdown"))) {
64 void* plugin_state = get_plugin_state(game->plugin);
65 plugin_call(game->plugin, plugin_shutdown, "shutdown", game, plugin_state);
66 set_plugin_state(game->plugin, 0);
67 }
68}
69
70/// Boot the game's plugin.
71static bool boot_plugin(Game* game) {
72 assert(game);
73 assert(game->plugin);
74 if (plugin_resolve(game->plugin, plugin_boot, "boot")) {
75 void* plugin_state = get_plugin_state(game->plugin);
76 return plugin_call(game->plugin, plugin_boot, "boot", game, plugin_state);
77 }
78 return true; // Plugin does not need to expose a boot().
79}
80
81/// Update the plugin's state.
82static void update_plugin(Game* game, double t, double dt) {
83 assert(game);
84 assert(game->plugin);
85 if (plugin_resolve(game->plugin, plugin_update, "update")) {
86 void* plugin_state = get_plugin_state(game->plugin);
87 plugin_call(
88 game->plugin, plugin_update, "update", game, plugin_state, t, dt);
89 }
90}
91
92/// Plugin render.
93static void render_plugin(const Game* game) {
94 assert(game);
95 assert(game->plugin);
96 if (plugin_resolve(game->plugin, plugin_render, "render")) {
97 void* plugin_state = get_plugin_state(game->plugin);
98 plugin_call(game->plugin, plugin_render, "render", game, plugin_state);
99 }
100}
101
102/// Plugin resize.
103static void resize_plugin(Game* game, int width, int height) {
104 assert(game);
105 assert(game->plugin);
106 if (plugin_resolve(game->plugin, plugin_resize, "resize")) {
107 void* plugin_state = get_plugin_state(game->plugin);
108 plugin_call(
109 game->plugin, plugin_resize, "resize", game, plugin_state, width,
110 height);
111 }
112}
113
114void app_end(Game* game);
115
116bool app_init(const GfxAppDesc* desc, void** app_state) {
117 assert(desc);
118
119 if (desc->argc <= 1) {
120 LOGE("Usage: %s <plugin> [plugin args]", desc->argv[0]);
121 return false;
122 }
123
124 Game* game = calloc(1, sizeof(Game));
125 if (!game) {
126 LOGE("Failed to allocate game state");
127 return false;
128 }
129
130 // Syntax: game <plugin> [plugin args]
131 //
132 // Here we consume the <plugin> arg so that plugins receive the remainder
133 // args starting from 0.
134 game->argc = desc->argc - 1;
135 game->argv = desc->argv + 1;
136
137 char exe_path_buf[NAME_MAX] = {0};
138 if (readlink("/proc/self/exe", exe_path_buf, sizeof(exe_path_buf)) == -1) {
139 LOGE("readlink(/proc/self/exe) failed");
140 goto cleanup;
141 }
142
143 // Replace the last / with a null terminator to remove the exe file from the
144 // path. This gets the file's parent directory.
145 *strrchr(exe_path_buf, '/') = 0;
146
147 const mstring exe_dir = mstring_make(exe_path_buf);
148 const mstring plugins_path = mstring_concat_cstr(exe_dir, "/src/plugins");
149
150 if (!(game->plugin_engine = new_plugin_engine(
151 &(PluginEngineDesc){.plugins_dir = mstring_cstr(&plugins_path)}))) {
152 goto cleanup;
153 }
154
155 const char* plugin = desc->argv[1];
156 if (!(game->plugin = load_plugin(game->plugin_engine, plugin))) {
157 goto cleanup;
158 }
159
160 if (!(game->gfx = gfx_init())) {
161 goto cleanup;
162 }
163
164 if (!init_plugin(game)) {
165 goto cleanup;
166 }
167 if (!boot_plugin(game)) {
168 goto cleanup;
169 }
170
171 *app_state = game;
172 return true;
173
174cleanup:
175 LOGE("Gfx error: %s", get_error());
176 app_end(game);
177 return false;
178}
179
180void app_end(Game* game) {
181 assert(game);
182 shutdown_plugin(game);
183 if (game->gfx) {
184 gfx_destroy(&game->gfx);
185 }
186 if (game->plugin) {
187 delete_plugin(&game->plugin);
188 }
189 if (game->plugin_engine) {
190 delete_plugin_engine(&game->plugin_engine);
191 }
192}
193
194void app_update(Game* game, double t, double dt) {
195 plugin_engine_update(game->plugin_engine);
196 if (plugin_reloaded(game->plugin)) {
197 shutdown_plugin(game);
198 const bool result = init_plugin(game);
199 assert(result); // TODO: handle error better.
200 }
201
202 update_plugin(game, t, dt);
203}
204
205void app_render(const Game* game) {
206 RenderBackend* render_backend = gfx_get_render_backend(game->gfx);
207 gfx_start_frame(render_backend);
208 render_plugin(game);
209 gfx_end_frame(render_backend);
210}
211
212void app_resize(Game* game, int width, int height) {
213 resize_plugin(game, width, height);
214}
215
216GFX_APP_MAIN(WIDTH, HEIGHT, MAX_FPS);