349 lines
9.6 KiB
C
349 lines
9.6 KiB
C
#include "common.h"
|
|
|
|
typedef struct game {
|
|
time_t mod_time;
|
|
memory_t memory;
|
|
const char* lib_name;
|
|
void* lib_ptr;
|
|
void (*startup)(memory_t*);
|
|
void (*shutdown)(void);
|
|
void (*update)(memory_t*, float, input_t, bool*);
|
|
void (*render)(void);
|
|
void (*debug)(uint64_t, uint64_t, uint64_t);
|
|
void (*audio_callback)(void* userdata, uint8_t* stream, int len);
|
|
} game_t;
|
|
|
|
typedef struct keystate {
|
|
int w; int a; int s; int d;
|
|
int up; int left; int down; int right;
|
|
int e;
|
|
int enter; int space; int esc;
|
|
} keystate_t;
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
(void)argc; (void)argv;
|
|
|
|
{
|
|
int ret;
|
|
|
|
ret = SDL_InitSubSystem(SDL_INIT_VIDEO);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Error initializing SDL Video subsystem: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ret = SDL_InitSubSystem(SDL_INIT_AUDIO);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "Error initializing SDL Audio subsystem: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
SDL_Window* window = NULL;
|
|
SDL_GLContext* context = NULL;
|
|
{
|
|
window = SDL_CreateWindow(
|
|
WINDOW_TITLE,
|
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
WINDOW_WIDTH, WINDOW_HEIGHT,
|
|
SDL_WINDOW_OPENGL);
|
|
|
|
if (window == NULL) {
|
|
fprintf(stderr, "Error creating SDL window: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
context = SDL_GL_CreateContext(window);
|
|
|
|
if (context == NULL) {
|
|
fprintf(stderr, "Error creating SDL GL context: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
SDL_GL_SetSwapInterval(0);
|
|
gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress);
|
|
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
|
|
game_t game = {0};
|
|
{
|
|
const char* platform = SDL_GetPlatform();
|
|
|
|
if (strcmp(platform, "Windows") == 0) {
|
|
game.lib_name = ".\\libgame.dll";
|
|
} else {
|
|
game.lib_name = "./libgame.so";
|
|
}
|
|
|
|
struct stat attrib;
|
|
stat(game.lib_name, &attrib);
|
|
game.mod_time = attrib.st_mtime;
|
|
|
|
game.lib_ptr = SDL_LoadObject(game.lib_name);
|
|
if (game.lib_ptr == NULL) {
|
|
fprintf(stderr, "Error loading game library: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.startup = SDL_LoadFunction(game.lib_ptr, "game_startup");
|
|
if (game.startup == NULL) {
|
|
fprintf(stderr, "Error loading game function pointer from game lib: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.shutdown = SDL_LoadFunction(game.lib_ptr, "game_shutdown");
|
|
if (game.shutdown == NULL) {
|
|
fprintf(stderr, "Error loading game function pointer from game lib: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.update = SDL_LoadFunction(game.lib_ptr, "game_update");
|
|
if (game.update == NULL) {
|
|
fprintf(stderr, "Error loading game function pointer from game lib: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.render = SDL_LoadFunction(game.lib_ptr, "game_render");
|
|
if (game.render == NULL) {
|
|
fprintf(stderr, "Error loading game function pointer from game lib: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.debug = SDL_LoadFunction(game.lib_ptr, "game_debug");
|
|
if (game.debug == NULL) {
|
|
fprintf(stderr, "Error loading game function pointer from game lib: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.audio_callback = SDL_LoadFunction(game.lib_ptr, "game_audio_callback");
|
|
if (game.audio_callback == NULL) {
|
|
fprintf(stderr, "Error loading game function pointer from game lib: %s\n", SDL_GetError());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.memory.engine.begin = calloc(ENGINE_MEMORY_SIZE, 1);
|
|
game.memory.engine.end = game.memory.engine.begin + ENGINE_MEMORY_SIZE;
|
|
game.memory.engine.current = game.memory.engine.begin;
|
|
game.memory.engine.size = ENGINE_MEMORY_SIZE;
|
|
if (game.memory.engine.begin == NULL) {
|
|
fprintf(stderr, "Error requesting %lu bytes for engine memory\n", ENGINE_MEMORY_SIZE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.memory.game.begin = calloc(GAME_MEMORY_SIZE, 1);
|
|
game.memory.game.end = game.memory.game.begin + GAME_MEMORY_SIZE;
|
|
game.memory.game.current = game.memory.game.begin;
|
|
game.memory.game.size = GAME_MEMORY_SIZE;
|
|
if (game.memory.game.begin == NULL) {
|
|
fprintf(stderr, "Error requesting %lu bytes for game memory\n", GAME_MEMORY_SIZE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
game.memory.scratch.begin = calloc(SCRATCH_MEMORY_SIZE, 1);
|
|
game.memory.scratch.end = game.memory.scratch.begin + SCRATCH_MEMORY_SIZE;
|
|
game.memory.scratch.current = game.memory.scratch.begin ;
|
|
game.memory.scratch.size = SCRATCH_MEMORY_SIZE;
|
|
if (game.memory.scratch.begin == NULL) {
|
|
fprintf(stderr, "Error requesting %lu bytes for scratch memory\n", SCRATCH_MEMORY_SIZE);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// Set up audio
|
|
SDL_AudioDeviceID audio_device;
|
|
SDL_AudioSpec audio_specs = {0};
|
|
{
|
|
audio_specs.freq = 8000;
|
|
audio_specs.format = AUDIO_F32;
|
|
audio_specs.channels = 2;
|
|
audio_specs.samples = 256;
|
|
audio_specs.callback = game.audio_callback;
|
|
audio_specs.userdata = &game.memory;
|
|
|
|
SDL_AudioSpec have;
|
|
audio_device = SDL_OpenAudioDevice(NULL, 0, &audio_specs, &have, 0);
|
|
|
|
// Un-pause
|
|
SDL_PauseAudioDevice(audio_device, 0);
|
|
}
|
|
|
|
game.startup(&game.memory);
|
|
|
|
bool is_running = true;
|
|
keystate_t keystate = {0};
|
|
uint64_t previous_time = SDL_GetTicks64();
|
|
while (is_running)
|
|
{
|
|
float dt;
|
|
uint16_t frame_time;
|
|
{
|
|
uint64_t current_time = SDL_GetTicks64();
|
|
frame_time = current_time - previous_time;
|
|
if (frame_time < TARGET_FRAME_TIME)
|
|
{
|
|
uint64_t sleep_time = TARGET_FRAME_TIME - frame_time;
|
|
SDL_Delay(sleep_time);
|
|
|
|
current_time = SDL_GetTicks64();
|
|
frame_time = current_time - previous_time;
|
|
}
|
|
|
|
previous_time = current_time;
|
|
dt = frame_time / 1000.0f;
|
|
}
|
|
|
|
{
|
|
SDL_Event e;
|
|
while (SDL_PollEvent(&e))
|
|
{
|
|
if (e.type == SDL_QUIT) {
|
|
is_running = false;
|
|
}
|
|
else if (e.type == SDL_KEYDOWN || e.type == SDL_KEYUP)
|
|
{
|
|
switch (e.key.keysym.sym)
|
|
{
|
|
case SDLK_w: {
|
|
keystate.w = e.type;
|
|
} break;
|
|
case SDLK_a: {
|
|
keystate.a = e.type;
|
|
} break;
|
|
case SDLK_s: {
|
|
keystate.s = e.type;
|
|
} break;
|
|
case SDLK_d: {
|
|
keystate.d = e.type;
|
|
} break;
|
|
case SDLK_UP: {
|
|
keystate.up = e.type;
|
|
} break;
|
|
case SDLK_LEFT: {
|
|
keystate.left = e.type;
|
|
} break;
|
|
case SDLK_DOWN: {
|
|
keystate.down = e.type;
|
|
} break;
|
|
case SDLK_RIGHT: {
|
|
keystate.right = e.type;
|
|
} break;
|
|
case SDLK_e: {
|
|
keystate.e = e.type;
|
|
} break;
|
|
case SDLK_RETURN: {
|
|
keystate.enter = e.type;
|
|
} break;
|
|
case SDLK_SPACE: {
|
|
keystate.space= e.type;
|
|
} break;
|
|
case SDLK_ESCAPE: {
|
|
keystate.esc = e.type;
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
input_t input = {0};
|
|
if (keystate.w == SDL_KEYDOWN || keystate.up == SDL_KEYDOWN) { input.up = true; }
|
|
if (keystate.a == SDL_KEYDOWN || keystate.left == SDL_KEYDOWN) { input.left = true; }
|
|
if (keystate.s == SDL_KEYDOWN || keystate.down == SDL_KEYDOWN) { input.down = true; }
|
|
if (keystate.d == SDL_KEYDOWN || keystate.right == SDL_KEYDOWN) { input.right = true; }
|
|
if (keystate.e == SDL_KEYDOWN) { input.interact = true; }
|
|
if (keystate.enter == SDL_KEYDOWN || keystate.space == SDL_KEYDOWN) { input.select = true; }
|
|
if (keystate.esc == SDL_KEYDOWN) { input.pause = true; }
|
|
SDL_GetRelativeMouseState(&input.mouse_x_delta, &input.mouse_y_delta);
|
|
|
|
bool quit_requested = false;
|
|
|
|
#ifdef BUILD_RELEASE
|
|
game.update(&game.memory, dt, input, &quit_requested);
|
|
game.render(&game.memory);
|
|
#else
|
|
{
|
|
struct stat attrib;
|
|
stat(game.lib_name, &attrib);
|
|
time_t mod_time = attrib.st_mtime;
|
|
if (mod_time != game.mod_time)
|
|
{
|
|
SDL_CloseAudioDevice(audio_device);
|
|
|
|
SDL_UnloadObject(game.lib_ptr);
|
|
game.lib_ptr = NULL;
|
|
game.update = NULL;
|
|
game.render = NULL;
|
|
game.debug = NULL;
|
|
game.audio_callback = NULL;
|
|
game.shutdown = NULL;
|
|
|
|
// Failure here is likely the first few times while it attempts to load a library
|
|
// that has not finished compiling, so we let it fail and try again next time
|
|
game.lib_ptr = SDL_LoadObject(game.lib_name);
|
|
if (game.lib_ptr)
|
|
{
|
|
game.mod_time = mod_time;
|
|
game.update = SDL_LoadFunction(game.lib_ptr, "game_update");
|
|
game.render = SDL_LoadFunction(game.lib_ptr, "game_render");
|
|
game.shutdown = SDL_LoadFunction(game.lib_ptr, "game_shutdown");
|
|
game.debug = SDL_LoadFunction(game.lib_ptr, "game_debug");
|
|
game.audio_callback = SDL_LoadFunction(game.lib_ptr, "game_audio_callback");
|
|
|
|
if (game.audio_callback)
|
|
{
|
|
// Need to re-initialize the audio device so it gets the updated callback pointer
|
|
audio_specs.callback = game.audio_callback;
|
|
|
|
SDL_AudioSpec have;
|
|
audio_device = SDL_OpenAudioDevice(NULL, 0, &audio_specs, &have, 0);
|
|
|
|
SDL_PauseAudioDevice(audio_device, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (game.update && game.render && game.debug)
|
|
{
|
|
uint64_t update_time;
|
|
{
|
|
uint64_t pre_time = SDL_GetTicks64();
|
|
game.update(&game.memory, dt, input, &quit_requested);
|
|
uint64_t post_time = SDL_GetTicks64();
|
|
update_time = post_time - pre_time;
|
|
}
|
|
|
|
uint16_t render_time;
|
|
{
|
|
uint64_t pre_time = SDL_GetTicks64();
|
|
game.render();
|
|
uint64_t post_time = SDL_GetTicks64();
|
|
render_time = post_time - pre_time;
|
|
}
|
|
|
|
game.debug(update_time, render_time, frame_time);
|
|
}
|
|
#endif
|
|
|
|
is_running = !quit_requested;
|
|
|
|
SDL_GL_SwapWindow(window);
|
|
}
|
|
|
|
game.shutdown();
|
|
|
|
SDL_UnloadObject(game.lib_ptr);
|
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
|
SDL_Quit();
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|