windows build compatibility, twn_game_object as abstraction for bridging game code and the engine
This commit is contained in:
		
							
								
								
									
										112
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								CMakeLists.txt
									
									
									
									
									
								
							| @@ -2,10 +2,6 @@ cmake_minimum_required(VERSION 3.21) | ||||
|  | ||||
| project(townengine LANGUAGES C) | ||||
|  | ||||
| list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) | ||||
|  | ||||
| set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | ||||
|  | ||||
| # dependencies | ||||
| find_package(SDL2 REQUIRED GLOBAL) | ||||
| find_package(SDL2_image REQUIRED GLOBAL) | ||||
| @@ -15,14 +11,14 @@ if (NOT CMAKE_BUILD_TYPE) | ||||
|         set(CMAKE_BUILD_TYPE Debug) | ||||
| endif () | ||||
|  | ||||
| set(TOWNENGINE_TARGET townengine CACHE INTERNAL "") | ||||
| set(TOWNENGINE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") | ||||
| set(TWN_TARGET townengine CACHE INTERNAL "") | ||||
| set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") | ||||
|  | ||||
| option(TOWNENGINE_HOT_RELOAD "Enable hot reloading support" ON) | ||||
| option(TOWNENGINE_ARCHIVE_DATA "Enable archival of assets" OFF) | ||||
| option(TWN_FEATURE_DYNLIB_GAME  "Enable dynamic library loading support" ON) | ||||
| option(TWN_ARCHIVE_DATA         "Enable archival of assets"              OFF) | ||||
|  | ||||
| # add -fPIC globally so that it's linked well | ||||
| add_compile_options($<$<BOOL:${TOWNENGINE_HOT_RELOAD}>:-fPIC>) | ||||
| add_compile_options($<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:-fPIC>) | ||||
|  | ||||
| set(PHYSFS_BUILD_SHARED FALSE) | ||||
| set(PHYSFS_DISABLE_INSTALL TRUE) | ||||
| @@ -45,12 +41,13 @@ else() | ||||
|         set(SYSTEM_SOURCE_FILES) | ||||
| endif() | ||||
|  | ||||
| set(TOWNENGINE_SOURCE_FILES | ||||
| set(TWN_SOURCE_FILES | ||||
|         third-party/physfs/extras/physfsrwops.c | ||||
|         third-party/stb/stb_vorbis.c | ||||
|         third-party/glad/src/glad.c | ||||
|  | ||||
|         townengine/main.c | ||||
|         townengine/twn_loop.c | ||||
|         townengine/twn_main.c | ||||
|         townengine/config.h | ||||
|         townengine/context/context.c townengine/context.h | ||||
|         townengine/audio/audio.c townengine/audio.h | ||||
| @@ -59,25 +56,29 @@ set(TOWNENGINE_SOURCE_FILES | ||||
|         townengine/input/input.c townengine/input.h | ||||
|         townengine/camera.c townengine/camera.h | ||||
|         townengine/textures/textures.c | ||||
|         townengine/twn_game_object.c | ||||
|  | ||||
|         $<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:townengine/twn_main.c> | ||||
|  | ||||
|         ${SYSTEM_SOURCE_FILES}) | ||||
|  | ||||
| list(TRANSFORM TOWNENGINE_SOURCE_FILES PREPEND ${TOWNENGINE_ROOT_DIR}/) | ||||
| list(TRANSFORM TWN_SOURCE_FILES PREPEND ${TWN_ROOT_DIR}/) | ||||
|  | ||||
| # base engine code, reused for games and tools | ||||
| if (TOWNENGINE_HOT_RELOAD) | ||||
|         add_library(${TOWNENGINE_TARGET} SHARED ${TOWNENGINE_SOURCE_FILES}) | ||||
| if (TWN_FEATURE_DYNLIB_GAME) | ||||
|         add_library(${TWN_TARGET} SHARED ${TWN_SOURCE_FILES}) | ||||
| else () | ||||
|         add_library(${TOWNENGINE_TARGET} STATIC ${TOWNENGINE_SOURCE_FILES}) | ||||
|         add_library(${TWN_TARGET} STATIC ${TWN_SOURCE_FILES}) | ||||
| endif () | ||||
| source_group(TREE ${TOWNENGINE_ROOT_DIR} FILES ${TOWNENGINE_SOURCE_FILES}) | ||||
|  | ||||
| set_target_properties(${TOWNENGINE_TARGET} PROPERTIES | ||||
| source_group(TREE ${TWN_ROOT_DIR} FILES ${TWN_SOURCE_FILES}) | ||||
|  | ||||
| set_target_properties(${TWN_TARGET} PROPERTIES | ||||
|                                            C_STANDARD 11 | ||||
|                                            C_STANDARD_REQUIRED ON | ||||
|                                            C_EXTENSIONS ON) # extensions are required by stb_ds.h | ||||
|  | ||||
| target_precompile_headers(${TOWNENGINE_TARGET} PRIVATE | ||||
| target_precompile_headers(${TWN_TARGET} PRIVATE | ||||
|         third-party/glad/include/glad/glad.h | ||||
|         third-party/stb/stb_ds.h) | ||||
|  | ||||
| @@ -163,7 +164,7 @@ function(give_options target) | ||||
|                                              ORGANIZATION_NAME="${ORGANIZATION_NAME}" | ||||
|                                              APP_NAME="${APP_NAME}" | ||||
|                                              PACKAGE_EXTENSION="${PACKAGE_EXTENSION}" | ||||
|                                              $<$<BOOL:${TOWNENGINE_HOT_RELOAD}>:HOT_RELOAD_SUPPORT>) | ||||
|                                              $<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>) | ||||
| endfunction() | ||||
|  | ||||
|  | ||||
| @@ -178,18 +179,17 @@ function(include_deps target) | ||||
|                 third-party/stb | ||||
|                 third-party/x-watcher) | ||||
|  | ||||
|         list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TOWNENGINE_ROOT_DIR}/) | ||||
|         list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/) | ||||
|         target_include_directories(${target} SYSTEM PRIVATE ${THIRD_PARTY_INCLUDES}) | ||||
|  | ||||
|         # allow access to headers from any point in source tree | ||||
|         target_include_directories(${target} PRIVATE ${TOWNENGINE_ROOT_DIR}) | ||||
|         target_include_directories(${target} PRIVATE ${TWN_ROOT_DIR}) | ||||
| endfunction() | ||||
|  | ||||
|  | ||||
| function(link_deps target) | ||||
|         target_link_libraries(${target} PUBLIC | ||||
|                                         SDL2::SDL2 | ||||
|                                         SDL2::SDL2main | ||||
|                                         SDL2_image::SDL2_image | ||||
|                                         SDL2_ttf::SDL2_ttf | ||||
|                                         physfs-static | ||||
| @@ -198,22 +198,24 @@ endfunction() | ||||
|  | ||||
|  | ||||
| function(use_townengine target sources output_directory data_dir) | ||||
|         if (TOWNENGINE_HOT_RELOAD) | ||||
|         if (TWN_FEATURE_DYNLIB_GAME) | ||||
|                 # game shared library, for reloading | ||||
|                 add_library(${target}_game SHARED ${sources}) | ||||
|                 set_target_properties(${target}_game PROPERTIES | ||||
|                                                      OUTPUT_NAME game | ||||
|                                                      LIBRARY_OUTPUT_DIRECTORY ${output_directory}) | ||||
|                 give_options(${target}_game) | ||||
|                 include_deps(${target}_game) | ||||
|                 target_link_libraries(${target}_game PUBLIC SDL2::SDL2) | ||||
|                 target_link_libraries(${target}_game PUBLIC SDL2::SDL2 ${TWN_TARGET}) | ||||
|                 set_target_properties(${target}_game PROPERTIES | ||||
|                                                      OUTPUT_NAME game | ||||
|                                                      LIBRARY_OUTPUT_DIRECTORY ${output_directory} | ||||
|                                                      RUNTIME_OUTPUT_DIRECTORY ${output_directory}) | ||||
|  | ||||
|                 # launcher binary, loads game and engine shared library | ||||
|                 add_executable(${target}_app ${TOWNENGINE_ROOT_DIR}/townengine/null.c) | ||||
|                 add_executable(${target}_app ${TWN_ROOT_DIR}/townengine/twn_main.c) | ||||
|  | ||||
|                 # todo: copy instead? | ||||
|                 # put libtownengine.so alongside the binary | ||||
|                 set_target_properties(${TOWNENGINE_TARGET} PROPERTIES | ||||
|                                                            LIBRARY_OUTPUT_DIRECTORY ${output_directory}) | ||||
|                 set_target_properties(${TWN_TARGET} PROPERTIES | ||||
|                                                     LIBRARY_OUTPUT_DIRECTORY ${output_directory}) | ||||
|         else () | ||||
|                 add_executable(${target}_app ${sources}) | ||||
|         endif () | ||||
| @@ -231,42 +233,51 @@ function(use_townengine target sources output_directory data_dir) | ||||
|         give_options(${target}_app) | ||||
|         include_deps(${target}_app) | ||||
|         link_deps(${target}_app) | ||||
|         target_link_libraries(${target}_app PUBLIC ${TOWNENGINE_TARGET}) | ||||
|         target_link_libraries(${target}_app PUBLIC ${TWN_TARGET}) | ||||
|         set_target_properties(${target}_app PROPERTIES | ||||
|                                             RUNTIME_OUTPUT_DIRECTORY ${output_directory}) | ||||
|  | ||||
|         # copy dlls for baby windows | ||||
|         add_custom_command(TARGET ${target}_app POST_BUILD | ||||
|                            COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:${target}_app> | ||||
|                                                                $<TARGET_RUNTIME_DLLS:${target}_app> | ||||
|                            COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:${target}_app> | ||||
|                                                                          $<TARGET_FILE_DIR:${target}_app> | ||||
|                            COMMAND_EXPAND_LISTS) | ||||
|  | ||||
|         if (UNIX) | ||||
|                 # create a bootstrapping script | ||||
|                 set(TOWNENGINE_BOOTSTRAP_EXEC_ARGS | ||||
|                         "$<IF:$<BOOL:${TOWNENGINE_ARCHIVE_DATA}>,--data-dir ./data.${PACKAGE_EXTENSION},--data-dir ${data_dir}>") | ||||
|         set(TWN_BOOTSTRAP_EXEC_ARGS | ||||
|                 "$<IF:$<BOOL:${TWN_ARCHIVE_DATA}>,--data-dir ./data.${PACKAGE_EXTENSION},--data-dir ${data_dir}>") | ||||
|  | ||||
|                 string(JOIN "\n" TOWNENGINE_BOOTSTRAP | ||||
|                                  "#!/bin/env sh" | ||||
|                                  "cd \"$(dirname \"$0\")\"" | ||||
|                                  "LD_LIBRARY_PATH=./ ./launcher ${TOWNENGINE_BOOTSTRAP_EXEC_ARGS}" | ||||
|         string(JOIN "\n" TWN_BOOTSTRAP_SH | ||||
|                          "#!/bin/env sh" | ||||
|                          "cd \"$(dirname \"$0\")\"" | ||||
|                          "LD_LIBRARY_PATH=./ ./launcher ${TWN_BOOTSTRAP_EXEC_ARGS}" | ||||
|                          "") | ||||
|  | ||||
|         FILE(GENERATE OUTPUT ${output_directory}/${target} | ||||
|              CONTENT "${TWN_BOOTSTRAP_SH}" | ||||
|              FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ) | ||||
|  | ||||
|         if (WIN32) | ||||
|                 string(JOIN "\n" TWN_BOOTSTRAP_BAT | ||||
|                                  "pushd \"%~dp0\"" | ||||
|                                  "launcher ${TWN_BOOTSTRAP_EXEC_ARGS}" | ||||
|                                  "") | ||||
|                 FILE(GENERATE OUTPUT ${output_directory}/${target} | ||||
|                      CONTENT "${TOWNENGINE_BOOTSTRAP}" | ||||
|  | ||||
|                 FILE(GENERATE OUTPUT ${output_directory}/${target}.bat | ||||
|                      CONTENT "${TWN_BOOTSTRAP_BAT}" | ||||
|                      FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ) | ||||
|         endif () | ||||
|  | ||||
|         if (TOWNENGINE_ARCHIVE_DATA) | ||||
|         if (TWN_ARCHIVE_DATA) | ||||
|                 # zip up assets | ||||
|                 add_custom_command(TARGET ${target}_app POST_BUILD | ||||
|                                    COMMAND cd ${data_dir} && zip -r ${output_directory}/data.${PACKAGE_EXTENSION} ./* | ||||
|                                    COMMAND cd ${data_dir} && ${CMAKE_COMMAND} -E tar "cf" ${output_directory}/data.${PACKAGE_EXTENSION} --format=zip ./ | ||||
|                                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
|         endif () | ||||
| endfunction() | ||||
|  | ||||
| give_options(${TOWNENGINE_TARGET}) | ||||
| include_deps(${TOWNENGINE_TARGET}) | ||||
| link_deps(${TOWNENGINE_TARGET}) | ||||
| give_options(${TWN_TARGET}) | ||||
| include_deps(${TWN_TARGET}) | ||||
| link_deps(${TWN_TARGET}) | ||||
|  | ||||
| # build the testgame if this cmake list is built directly | ||||
| if (${CMAKE_PROJECT_NAME} MATCHES townengine) | ||||
| @@ -274,6 +285,7 @@ if (${CMAKE_PROJECT_NAME} MATCHES townengine) | ||||
| endif () | ||||
|  | ||||
| # move compie_commands.json into root directory so that it plays nicer with editors | ||||
| add_custom_command(TARGET ${TOWNENGINE_TARGET} POST_BUILD | ||||
| set(CMAKE_EXPORT_COMPILE_COMMANDS ON) | ||||
| add_custom_command(TARGET ${TWN_TARGET} POST_BUILD | ||||
|                    COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/compile_commands.json | ||||
|                                                                  ${TOWNENGINE_ROOT_DIR}) | ||||
|                                                                  ${TWN_ROOT_DIR}) | ||||
|   | ||||
| @@ -5,6 +5,8 @@ | ||||
|  | ||||
| #include "townengine/game_api.h" | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
|  | ||||
| static void title_tick(struct state *state) { | ||||
|     struct scene_title *scn = (struct scene_title *)state->scene; | ||||
| @@ -20,9 +22,9 @@ static void title_tick(struct state *state) { | ||||
|      | ||||
|      | ||||
|     /* draw the tick count as an example of dynamic text */ | ||||
|     size_t text_str_len = snprintf(NULL, 0, "%ld", state->ctx->tick_count) + 1; | ||||
|     size_t text_str_len = snprintf(NULL, 0, "%lld", state->ctx->tick_count) + 1; | ||||
|     char *text_str = cmalloc(text_str_len); | ||||
|     snprintf(text_str, text_str_len, "%ld", state->ctx->tick_count); | ||||
|     snprintf(text_str, text_str_len, "%lld", state->ctx->tick_count); | ||||
|  | ||||
|     const char *font = "fonts/kenney-pixel.ttf"; | ||||
|     int text_h = 32; | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #ifndef AUDIO_H | ||||
| #define AUDIO_H | ||||
|  | ||||
| #include "twn_engine_api.h" | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
|  | ||||
| @@ -23,14 +25,14 @@ typedef struct play_audio_args { | ||||
| /* path must contain valid file extension to infer which file format it is */ | ||||
| /* supported formats: .ogg, .xm */ | ||||
| /* preserves args that are already specified on the channel */ | ||||
| void play_audio(const char *path, const char *channel); | ||||
| TWN_API void play_audio(const char *path, const char *channel); | ||||
|  | ||||
| void play_audio_ex(const char *path, const char *channel, t_play_audio_args args); | ||||
| TWN_API void play_audio_ex(const char *path, const char *channel, t_play_audio_args args); | ||||
|  | ||||
| /* could be used for modifying args */ | ||||
| /* warn: is only valid if no other calls to audio are made */ | ||||
| t_play_audio_args *get_audio_args(const char *channel); | ||||
| TWN_API t_play_audio_args *get_audio_args(const char *channel); | ||||
|  | ||||
| t_play_audio_args get_default_audio_args(void); | ||||
| TWN_API t_play_audio_args get_default_audio_args(void); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "rendering/internal_api.h" | ||||
| #include "textures/internal_api.h" | ||||
| #include "input.h" | ||||
| #include "twn_engine_api.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
|  | ||||
| @@ -65,6 +66,6 @@ typedef struct context { | ||||
|     bool initialization_needed; | ||||
| } t_ctx; | ||||
|  | ||||
| extern t_ctx ctx; | ||||
| TWN_API extern t_ctx ctx; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -8,15 +8,15 @@ | ||||
| #include "townengine/audio.h" | ||||
| #include "townengine/util.h" | ||||
| #include "townengine/input.h" | ||||
| #include "twn_engine_api.h" | ||||
|  | ||||
|  | ||||
| #ifndef HOT_RELOAD_SUPPORT | ||||
| /* sole game logic and display function. | ||||
|    all state must be used from and saved to supplied state pointer. */ | ||||
| extern void game_tick(void); | ||||
| TWN_API extern void game_tick(void); | ||||
|  | ||||
| /* called when application is closing. */ | ||||
| extern void game_end(void); | ||||
| #endif | ||||
| TWN_API extern void game_end(void); | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										137
									
								
								townengine/game_object/twn_linux_game_object_c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								townengine/game_object/twn_linux_game_object_c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| #include "townengine/twn_game_object.h" | ||||
| #include "townengine/context.h" | ||||
|  | ||||
| #include <x-watcher.h> | ||||
| #include <SDL2/SDL.h> | ||||
|  | ||||
| #include <dlfcn.h> | ||||
|  | ||||
|  | ||||
| #define GAME_OBJECT_PATH "./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 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; | ||||
|     } | ||||
|  | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     handle = new_handle; | ||||
|  | ||||
|     if (ctx.tick_count != 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_MODIFIED: | ||||
|             SDL_LockMutex(lock); | ||||
|             last_tick_modified = ctx.tick_count; | ||||
|             loaded_after_modification = false; | ||||
|             SDL_UnlockMutex(lock); | ||||
|             break; | ||||
|  | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| void game_object_load(void) { | ||||
|     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.tick_count - 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(); | ||||
| } | ||||
							
								
								
									
										21
									
								
								townengine/game_object/twn_static_game_object_c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								townengine/game_object/twn_static_game_object_c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| #include "townengine/twn_game_object.h" | ||||
| #include "townengine/game_api.h" | ||||
|  | ||||
|  | ||||
| void game_object_load(void) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void game_object_unload(void) { | ||||
|     game_end(); | ||||
| } | ||||
|  | ||||
|  | ||||
| bool game_object_try_reloading(void) { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
|  | ||||
| void game_object_tick(void) { | ||||
|     game_tick(); | ||||
| } | ||||
							
								
								
									
										83
									
								
								townengine/game_object/twn_win32_game_object_c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								townengine/game_object/twn_win32_game_object_c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| #include "townengine/twn_game_object.h" | ||||
| #include "townengine/context.h" | ||||
|  | ||||
| #include <errhandlingapi.h> | ||||
| #include <libloaderapi.h> | ||||
|  | ||||
|  | ||||
| #define GAME_OBJECT_PATH "libgame.dll" | ||||
|  | ||||
|  | ||||
| static void (*game_tick_callback)(void); | ||||
| static void (*game_end_callback)(void); | ||||
|  | ||||
| static void *handle = NULL; | ||||
|  | ||||
|  | ||||
| static void load_game_object(void) { | ||||
|     /* needs to be closed otherwise symbols aren't resolved again */ | ||||
|     if (handle) { | ||||
|         FreeLibrary(handle); | ||||
|         handle = NULL; | ||||
|         game_tick_callback = NULL; | ||||
|         game_end_callback = NULL; | ||||
|     } | ||||
|  | ||||
|     void *new_handle = LoadLibraryA(GAME_OBJECT_PATH); | ||||
|     if (!new_handle) { | ||||
|         log_critical("Hot Reload Error: Cannot open game code shared object (%s)", GetLastError()); | ||||
|         goto ERR_OPENING_SO; | ||||
|     } | ||||
|  | ||||
|     game_tick_callback = (void (*)(void))GetProcAddress(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))GetProcAddress(new_handle, "game_end"); | ||||
|     if (!game_end_callback) { | ||||
|         CRY("Hot Reload Error", "game_end_callback() symbol wasn't found"); | ||||
|         goto ERR_GETTING_PROC; | ||||
|     } | ||||
|  | ||||
|     handle = new_handle; | ||||
|  | ||||
|     if (ctx.tick_count != 0) | ||||
|         log_info("Game object was reloaded\n"); | ||||
|  | ||||
|     return; | ||||
|  | ||||
| ERR_GETTING_PROC: | ||||
|     FreeLibrary(new_handle); | ||||
|     game_tick_callback = NULL; | ||||
|     game_end_callback = NULL; | ||||
|  | ||||
| ERR_OPENING_SO: | ||||
|     die_abruptly(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void game_object_load(void) { | ||||
|     load_game_object(); | ||||
| } | ||||
|  | ||||
|  | ||||
| void game_object_unload(void) { | ||||
|     game_end_callback(); | ||||
|     FreeLibrary(handle); | ||||
|     handle = NULL; | ||||
|     game_tick_callback = NULL; | ||||
|     game_end_callback = NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* doesn't support reloading because of problems with file watching */ | ||||
| bool game_object_try_reloading(void) { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
|  | ||||
| void game_object_tick(void) { | ||||
|     game_tick_callback(); | ||||
| } | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "config.h" | ||||
| #include "vec.h" | ||||
| #include "util.h" | ||||
| #include "twn_engine_api.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
|  | ||||
| @@ -68,33 +69,33 @@ struct input_state { | ||||
| }; | ||||
|  | ||||
|  | ||||
| void input_state_init(struct input_state *input); | ||||
| void input_state_deinit(struct input_state *input); | ||||
| void input_state_update(struct input_state *input); | ||||
| TWN_API void input_state_init(struct input_state *input); | ||||
| TWN_API void input_state_deinit(struct input_state *input); | ||||
| TWN_API void input_state_update(struct input_state *input); | ||||
|  | ||||
| void input_bind_action_scancode(struct input_state *input, | ||||
| TWN_API void input_bind_action_scancode(struct input_state *input, | ||||
|                                 char *action_name, | ||||
|                                 SDL_Scancode scancode); | ||||
| void input_unbind_action_scancode(struct input_state *input, | ||||
| TWN_API void input_unbind_action_scancode(struct input_state *input, | ||||
|                                   char *action_name, | ||||
|                                   SDL_Scancode scancode); | ||||
| void input_bind_action_mouse(struct input_state *input, | ||||
| TWN_API void input_bind_action_mouse(struct input_state *input, | ||||
|                              char *action_name, | ||||
|                              uint8_t mouse_button); | ||||
| void input_unbind_action_mouse(struct input_state *input, | ||||
| TWN_API void input_unbind_action_mouse(struct input_state *input, | ||||
|                                char *action_name, | ||||
|                                uint8_t mouse_button); | ||||
|  | ||||
| void input_add_action(struct input_state *input, char *action_name); | ||||
| void input_delete_action(struct input_state *input, char *action_name); | ||||
| TWN_API void input_add_action(struct input_state *input, char *action_name); | ||||
| TWN_API void input_delete_action(struct input_state *input, char *action_name); | ||||
|  | ||||
| bool input_is_action_pressed(struct input_state *input, char *action_name); | ||||
| bool input_is_action_just_pressed(struct input_state *input, char *action_name); | ||||
| bool input_is_action_just_released(struct input_state *input, char *action_name); | ||||
| TWN_API bool input_is_action_pressed(struct input_state *input, char *action_name); | ||||
| TWN_API bool input_is_action_just_pressed(struct input_state *input, char *action_name); | ||||
| TWN_API bool input_is_action_just_released(struct input_state *input, char *action_name); | ||||
|  | ||||
| t_fvec2 input_get_action_position(struct input_state *input, char *action_name); | ||||
| TWN_API t_fvec2 input_get_action_position(struct input_state *input, char *action_name); | ||||
|  | ||||
| void input_set_mouse_captured(struct input_state *input, bool value); | ||||
| bool input_is_mouse_captured(struct input_state *input); | ||||
| TWN_API void input_set_mouse_captured(struct input_state *input, bool value); | ||||
| TWN_API bool input_is_mouse_captured(struct input_state *input); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,2 +0,0 @@ | ||||
| /* cmake nonsense */ | ||||
| struct a { int b; }; | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "util.h" | ||||
| #include "macros/option.h" | ||||
| #include "camera.h" | ||||
| #include "twn_engine_api.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
|  | ||||
| @@ -25,24 +26,24 @@ typedef struct push_sprite_args { | ||||
| /* pushes a sprite onto the sprite render queue */ | ||||
| /* this is a simplified version of push_sprite_ex for the most common case. */ | ||||
| /* it assumes you want no color modulation, no rotation, no flip */ | ||||
| void push_sprite(t_push_sprite_args args); | ||||
| TWN_API void push_sprite(t_push_sprite_args args); | ||||
| #define m_sprite(...) (push_sprite((t_push_sprite_args){__VA_ARGS__})) | ||||
|  | ||||
| /* pushes a filled rectangle onto the rectangle render queue */ | ||||
| void push_rectangle(t_frect rect, t_color color); | ||||
| TWN_API void push_rectangle(t_frect rect, t_color color); | ||||
|  | ||||
| /* pushes a filled circle onto the circle render queue */ | ||||
| void push_circle(t_fvec2 position, float radius, t_color color); | ||||
| TWN_API void push_circle(t_fvec2 position, float radius, t_color color); | ||||
|  | ||||
| void text_cache_init(struct text_cache *cache); | ||||
| void text_cache_deinit(struct text_cache *cache); | ||||
| void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path); | ||||
| int get_text_width(char *string, int height_px, const char *font_path); | ||||
| TWN_API void text_cache_init(struct text_cache *cache); | ||||
| TWN_API void text_cache_deinit(struct text_cache *cache); | ||||
| TWN_API void push_text(char *string, t_fvec2 position, int height_px, t_color color, const char *font_path); | ||||
| TWN_API int get_text_width(char *string, int height_px, const char *font_path); | ||||
|  | ||||
| /* pushes a textured 3d triangle onto the render queue */ | ||||
| /* vertices are in absolute coordinates, relative to world origin */ | ||||
| /* texture coordinates are in pixels */ | ||||
| void unfurl_triangle(const char *path, | ||||
| TWN_API void unfurl_triangle(const char *path, | ||||
|                      t_fvec3 v0, | ||||
|                      t_fvec3 v1, | ||||
|                      t_fvec3 v2, | ||||
| @@ -70,6 +71,6 @@ void unfurl_triangle(const char *path, | ||||
| //                      t_frect uvs); | ||||
|  | ||||
| /* pushes a camera state to be used for all future unfurl_* commands */ | ||||
| void set_camera(const t_camera *camera); | ||||
| TWN_API void set_camera(const t_camera *camera); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
|  | ||||
| #include "../util.h" | ||||
| #include "../textures/modes.h" | ||||
| #include "../twn_engine_api.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
| #include <stb_rect_pack.h> | ||||
| @@ -46,11 +47,11 @@ typedef struct { uint16_t id; } t_texture_key; | ||||
| /* tests whether given key structure corresponds to any texture */ | ||||
| #define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1) | ||||
|  | ||||
| void textures_cache_init(struct texture_cache *cache, SDL_Window *window); | ||||
| void textures_cache_deinit(struct texture_cache *cache); | ||||
| TWN_API void textures_cache_init(struct texture_cache *cache, SDL_Window *window); | ||||
| TWN_API void textures_cache_deinit(struct texture_cache *cache); | ||||
|  | ||||
| /* for debugging */ | ||||
| void textures_dump_atlases(struct texture_cache *cache); | ||||
| TWN_API void textures_dump_atlases(struct texture_cache *cache); | ||||
|  | ||||
| /* loads an image if it isn't in the cache, otherwise a no-op. */ | ||||
| /* can be called from anywhere at any time after init, useful if you want to */ | ||||
| @@ -60,33 +61,34 @@ void textures_dump_atlases(struct texture_cache *cache); | ||||
| /* repacks the current texture atlas based on the texture cache if needed */ | ||||
| /* any previously returned srcrect results are invalidated after that */ | ||||
| /* call it every time before rendering */ | ||||
| void textures_update_atlas(struct texture_cache *cache); | ||||
| TWN_API void textures_update_atlas(struct texture_cache *cache); | ||||
|  | ||||
| /* returns a persistent handle to some texture in cache, loading it if needed */ | ||||
| /* check the result with m_texture_key_is_valid() */ | ||||
| t_texture_key textures_get_key(struct texture_cache *cache, const char *path); | ||||
| TWN_API t_texture_key textures_get_key(struct texture_cache *cache, const char *path); | ||||
|  | ||||
| /* returns a rect in a texture cache of the given key */ | ||||
| t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key); | ||||
| TWN_API t_frect textures_get_srcrect(const struct texture_cache *cache, t_texture_key key); | ||||
|  | ||||
| /* returns a rect of dimensions of the whole texture (whole atlas) */ | ||||
| t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key); | ||||
| TWN_API t_frect textures_get_dims(const struct texture_cache *cache, t_texture_key key); | ||||
|  | ||||
| /* returns an identifier that is equal for all textures placed in the same atlas */ | ||||
| int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key); | ||||
| TWN_API int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key key); | ||||
|  | ||||
| /* binds atlas texture in opengl state */ | ||||
| void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target); | ||||
| TWN_API void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target); | ||||
|  | ||||
| /* binds texture in opengl state, ensuring that it's usable with texture repeat */ | ||||
| void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key, GLenum target); | ||||
| TWN_API void textures_bind_repeating(const struct texture_cache *cache, t_texture_key key, GLenum target); | ||||
|  | ||||
| /* returns helpful information about contents of alpha channel in given texture */ | ||||
| enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key); | ||||
| TWN_API enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key); | ||||
|  | ||||
| /* returns the number of atlases in the cache */ | ||||
| size_t textures_get_num_atlases(const struct texture_cache *cache); | ||||
| TWN_API size_t textures_get_num_atlases(const struct texture_cache *cache); | ||||
|  | ||||
| void textures_reset_state(void); | ||||
| /* TODO: should recieve texture_cache, get_key optimization cache should be cleared some other way */ | ||||
| TWN_API void textures_reset_state(void); | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										10
									
								
								townengine/twn_engine_api.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								townengine/twn_engine_api.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| #ifndef TWN_ENGINE_API_H | ||||
| #define TWN_ENGINE_API_H | ||||
|  | ||||
| #if defined(__WIN32) | ||||
| 	#define TWN_API __declspec(dllexport) | ||||
| #else | ||||
| 	#define TWN_API | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										12
									
								
								townengine/twn_game_object.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								townengine/twn_game_object.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #if defined(TWN_FEATURE_DYNLIB_GAME) | ||||
|     #if defined(__linux__) | ||||
|         #include "game_object/twn_linux_game_object_c.h" | ||||
|     #elif defined(_WIN32) | ||||
|         #include "game_object/twn_win32_game_object_c.h" | ||||
|     #else | ||||
|         #warning "TWN_FEATURE_DYNLIB_GAME is set, but not supported" | ||||
|         #include "game_object/twn_static_game_object_c.h" | ||||
|     #endif | ||||
| #else | ||||
|     #include "game_object/twn_static_game_object_c.h" | ||||
| #endif | ||||
							
								
								
									
										22
									
								
								townengine/twn_game_object.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								townengine/twn_game_object.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| #ifndef GAME_OBJECT_H | ||||
| #define GAME_OBJECT_H | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| /* | ||||
|  * game object provides an interface for bridging between game code and engine. | ||||
|  * for example, it might implement dynamic load libraries with hot reloading. | ||||
|  */ | ||||
|  | ||||
|  | ||||
| void game_object_load(void); | ||||
|  | ||||
| void game_object_unload(void); | ||||
|  | ||||
| /* returns true if reload happened, otherwise false */ | ||||
| bool game_object_try_reloading(void); | ||||
|  | ||||
| void game_object_tick(void); | ||||
|  | ||||
|  | ||||
| #endif | ||||
| @@ -1,8 +1,9 @@ | ||||
| #include "twn_loop.h" | ||||
| #include "townengine/context.h" | ||||
| #include "townengine/rendering.h" | ||||
| #include "townengine/input/internal_api.h" | ||||
| #include "townengine/util.h" | ||||
| #include "townengine/game_api.h" | ||||
| #include "townengine/twn_game_object.h" | ||||
| #include "townengine/audio/internal_api.h" | ||||
| #include "townengine/textures/internal_api.h" | ||||
| #include "townengine/rendering/internal_api.h" | ||||
| @@ -20,30 +21,6 @@ | ||||
| #include <tgmath.h> | ||||
| #include <limits.h> | ||||
| 
 | ||||
| #ifdef HOT_RELOAD_SUPPORT | ||||
| #include <x-watcher.h> | ||||
| #include <dlfcn.h> | ||||
| 
 | ||||
| #ifdef __linux__ | ||||
|     #define GAME_OBJECT_PATH "./libgame.so" | ||||
| #else | ||||
|     #define GAME_OBJECT_PATH "game.dll" | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #ifdef HOT_RELOAD_SUPPORT | ||||
| 
 | ||||
| static void (*game_tick)(void); | ||||
| static void (*game_end)(void); | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| /* is used to lock x-watcher driven hot reloading */ | ||||
| static uint64_t game_object_last_time_modified; | ||||
| static bool game_object_loaded_after_modification = true; | ||||
| static SDL_mutex *game_object_mutex; | ||||
| 
 | ||||
| 
 | ||||
| static void poll_events(void) { | ||||
|     SDL_Event e; | ||||
| @@ -192,7 +169,7 @@ static void main_loop(void) { | ||||
| 
 | ||||
|             input_state_update(&ctx.input); | ||||
| 
 | ||||
|             game_tick(); | ||||
|             game_object_tick(); | ||||
| 
 | ||||
|             ctx.frame_accumulator -= ctx.desired_frametime; | ||||
|             ctx.tick_count = (ctx.tick_count % ULLONG_MAX) + 1; | ||||
| @@ -375,8 +352,6 @@ static bool initialize(void) { | ||||
|     } | ||||
|     */ | ||||
| 
 | ||||
|     game_object_mutex = SDL_CreateMutex(); | ||||
| 
 | ||||
|     return true; | ||||
| 
 | ||||
| fail: | ||||
| @@ -404,8 +379,6 @@ static void clean_up(void) { | ||||
|     text_cache_deinit(&ctx.text_cache); | ||||
|     textures_cache_deinit(&ctx.texture_cache); | ||||
| 
 | ||||
|     SDL_DestroyMutex(game_object_mutex); | ||||
| 
 | ||||
|     PHYSFS_deinit(); | ||||
|     TTF_Quit(); | ||||
|     IMG_Quit(); | ||||
| @@ -413,114 +386,13 @@ static void clean_up(void) { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef HOT_RELOAD_SUPPORT | ||||
| 
 | ||||
| static void load_game_shared_object(void) { | ||||
| #ifdef __linux__ | ||||
|     static void *handle = NULL; | ||||
| 
 | ||||
|     SDL_LockMutex(game_object_mutex); | ||||
| 
 | ||||
|     /* needs to be closed otherwise symbols aren't resolved again */ | ||||
|     if (handle) { | ||||
|         dlclose(handle); | ||||
|         handle = NULL; | ||||
|         game_tick = NULL; | ||||
|         game_end = 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; | ||||
|     } | ||||
| 
 | ||||
|     game_tick = (void (*)(void))dlsym(new_handle, "game_tick"); | ||||
|     if (!game_tick) { | ||||
|         CRY("Hot Reload Error", "game_tick() symbol wasn't found"); | ||||
|         goto ERR_GETTING_PROC; | ||||
|     } | ||||
| 
 | ||||
|     game_end = (void (*)(void))dlsym(new_handle, "game_end"); | ||||
|     if (!game_end) { | ||||
|         CRY("Hot Reload Error", "game_end() symbol wasn't found"); | ||||
|         goto ERR_GETTING_PROC; | ||||
|     } | ||||
| 
 | ||||
|     handle = new_handle; | ||||
| 
 | ||||
|     SDL_UnlockMutex(game_object_mutex); | ||||
| 
 | ||||
|     log_info("Game code hot loaded\n"); | ||||
| 
 | ||||
|     return; | ||||
| 
 | ||||
| ERR_GETTING_PROC: | ||||
|     dlclose(new_handle); | ||||
|     game_tick = NULL; | ||||
|     game_end = NULL; | ||||
| 
 | ||||
| ERR_OPENING_SO: | ||||
|     SDL_UnlockMutex(game_object_mutex); | ||||
| 
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void hot_reload_callback(XWATCHER_FILE_EVENT event, | ||||
|                                 const char *path, | ||||
|                                 int context, | ||||
|                                 void *data) | ||||
| { | ||||
|     (void)context; | ||||
|     (void)path; | ||||
|     (void)data; | ||||
| 
 | ||||
|     switch(event) { | ||||
|         case XWATCHER_FILE_MODIFIED: | ||||
|             SDL_LockMutex(game_object_mutex); | ||||
|             game_object_last_time_modified = ctx.tick_count; | ||||
|             game_object_loaded_after_modification = false; | ||||
|             SDL_UnlockMutex(game_object_mutex); | ||||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| static void load_game_shared_object(void) {} | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| static void load_game(void) { | ||||
| #ifdef HOT_RELOAD_SUPPORT | ||||
|     x_watcher *watcher = xWatcher_create(); | ||||
| 
 | ||||
|     xWatcher_reference dir; | ||||
|     dir.path = GAME_OBJECT_PATH; | ||||
|     dir.callback_func = hot_reload_callback; | ||||
| 
 | ||||
|     xWatcher_appendFile(watcher, &dir); | ||||
|     xWatcher_start(watcher); | ||||
| 
 | ||||
|     // xWatcher_destroy(watcher);
 | ||||
| 
 | ||||
|     load_game_shared_object(); | ||||
| 
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void reset_state(void) { | ||||
|     input_reset_state(&ctx.input); | ||||
|     textures_reset_state(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int main(int argc, char **argv) { | ||||
| int enter_loop(int argc, char **argv) { | ||||
|     ctx.argc = argc; | ||||
|     ctx.argv = argv; | ||||
| 
 | ||||
| @@ -537,26 +409,21 @@ int main(int argc, char **argv) { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     load_game(); | ||||
|     game_object_load(); | ||||
| 
 | ||||
|     ctx.was_successful = true; | ||||
|     ctx.initialization_needed = true; | ||||
| 
 | ||||
|     while (ctx.is_running) { | ||||
|         /* only load the modified library after some time, as compilers make a lot of modifications */ | ||||
|         SDL_LockMutex(game_object_mutex); | ||||
|         if (ctx.tick_count - game_object_last_time_modified > 10 && !game_object_loaded_after_modification) { | ||||
|             load_game_shared_object(); | ||||
|             game_object_loaded_after_modification = true; | ||||
|         if (game_object_try_reloading()) { | ||||
|             ctx.initialization_needed = true; | ||||
|             reset_state(); | ||||
|         } | ||||
|         SDL_UnlockMutex(game_object_mutex); | ||||
| 
 | ||||
|         main_loop(); | ||||
|     } | ||||
| 
 | ||||
|     game_end(); | ||||
|     game_object_unload(); | ||||
| 
 | ||||
|     clean_up(); | ||||
| 
 | ||||
							
								
								
									
										10
									
								
								townengine/twn_loop.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								townengine/twn_loop.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| #ifndef TWN_LOOP_H | ||||
| #define TWN_LOOP_H | ||||
|  | ||||
| #include "twn_engine_api.h" | ||||
|  | ||||
|  | ||||
| TWN_API int enter_loop(int argc, char **argv); | ||||
|  | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										11
									
								
								townengine/twn_main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								townengine/twn_main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #include "twn_loop.h" | ||||
|  | ||||
| #define SDL_MAIN_HANDLED | ||||
| #include <SDL2/SDL.h> | ||||
|  | ||||
|  | ||||
| int main(int argc, char **argv) { | ||||
|     SDL_SetMainReady(); | ||||
|  | ||||
|     enter_loop(argc, argv); | ||||
| } | ||||
| @@ -2,6 +2,7 @@ | ||||
| #define UTIL_H | ||||
|  | ||||
| #include "townengine/vec.h" | ||||
| #include "twn_engine_api.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
| #include <physfs.h> | ||||
| @@ -17,28 +18,28 @@ | ||||
| /* GENERAL UTILITIES */ | ||||
| /*                   */ | ||||
|  | ||||
| void cry_impl(const char *file, const int line, const char *title, const char *text); | ||||
| TWN_API void cry_impl(const char *file, const int line, const char *title, const char *text); | ||||
| #define CRY(title, text) cry_impl(__FILE__, __LINE__, title, text) | ||||
| #define CRY_SDL(title) cry_impl(__FILE__, __LINE__, title, SDL_GetError()) | ||||
| #define CRY_PHYSFS(title)                                               \ | ||||
| cry_impl(__FILE__, __LINE__, title, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())) | ||||
|  | ||||
|  | ||||
| void log_info(const char *restrict format, ...); | ||||
| void log_critical(const char *restrict format, ...); | ||||
| void log_warn(const char *restrict format, ...); | ||||
| TWN_API void log_info(const char *restrict format, ...); | ||||
| TWN_API void log_critical(const char *restrict format, ...); | ||||
| TWN_API void log_warn(const char *restrict format, ...); | ||||
|  | ||||
|  | ||||
| /* for when there's absolutely no way to continue */ | ||||
| noreturn void die_abruptly(void); | ||||
| TWN_API noreturn void die_abruptly(void); | ||||
|  | ||||
|  | ||||
| /* "critical" allocation functions which will log and abort() on failure. */ | ||||
| /* if it is reasonable to handle this gracefully, use the standard versions. */ | ||||
| /* the stb implementations will be configured to use these */ | ||||
| void *cmalloc(size_t size); | ||||
| void *crealloc(void *ptr, size_t size); | ||||
| void *ccalloc(size_t num, size_t size); | ||||
| TWN_API void *cmalloc(size_t size); | ||||
| TWN_API void *crealloc(void *ptr, size_t size); | ||||
| TWN_API void *ccalloc(size_t num, size_t size); | ||||
|  | ||||
|  | ||||
| /* DON'T FORGET ABOUT DOUBLE EVALUATION */ | ||||
| @@ -58,20 +59,20 @@ void *ccalloc(size_t num, size_t size); | ||||
| #define RAD2DEG (180 / M_PI) | ||||
|  | ||||
| /* TODO: this is why generics were invented. sorry, i'm tired today */ | ||||
| double clamp(double d, double min, double max); | ||||
| float clampf(float f, float min, float max); | ||||
| int clampi(int i, int min, int max); | ||||
| TWN_API double clamp(double d, double min, double max); | ||||
| TWN_API float clampf(float f, float min, float max); | ||||
| TWN_API int clampi(int i, int min, int max); | ||||
|  | ||||
| /* sets buf_out to a pointer to a byte buffer which must be freed. */ | ||||
| /* returns the size of this buffer. */ | ||||
| int64_t file_to_bytes(const char *path, unsigned char **buf_out); | ||||
| TWN_API int64_t file_to_bytes(const char *path, unsigned char **buf_out); | ||||
|  | ||||
| /* returns a pointer to a string which must be freed */ | ||||
| char *file_to_str(const char *path); | ||||
| TWN_API char *file_to_str(const char *path); | ||||
|  | ||||
|  | ||||
| /* returns true if str ends with suffix */ | ||||
| bool strends(const char *str, const char *suffix); | ||||
| TWN_API TWN_API bool strends(const char *str, const char *suffix); | ||||
|  | ||||
|  | ||||
| /*                      */ | ||||
| @@ -108,14 +109,14 @@ _Alignas(16) | ||||
| } t_frect; | ||||
|  | ||||
|  | ||||
| bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result); | ||||
| TWN_API bool intersect_rect(const t_rect *a, const t_rect *b, t_rect *result); | ||||
|  | ||||
| bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); | ||||
| TWN_API bool intersect_frect(const t_frect *a, const t_frect *b, t_frect *result); | ||||
|  | ||||
| /* TODO: generics and specials (see m_to_fvec2() for an example)*/ | ||||
| t_frect to_frect(t_rect rect); | ||||
| TWN_API t_frect to_frect(t_rect rect); | ||||
|  | ||||
| t_fvec2 frect_center(t_frect rect); | ||||
| TWN_API t_fvec2 frect_center(t_frect rect); | ||||
|  | ||||
|  | ||||
| typedef struct matrix4 { | ||||
| @@ -129,16 +130,16 @@ typedef struct matrix4 { | ||||
|  * example: | ||||
|  * tick_timer(&player->jump_air_timer); | ||||
|  */ | ||||
| void tick_timer(int *value); | ||||
| TWN_API void tick_timer(int *value); | ||||
|  | ||||
| /* decrements a floating point second-based timer, stopping at 0.0 */ | ||||
| /* meant for poll based real time logic in game logic */ | ||||
| /* note that it should be decremented only on the next tick after its creation */ | ||||
| void tick_ftimer(float *value); | ||||
| TWN_API void tick_ftimer(float *value); | ||||
|  | ||||
| /* same as `tick_ftimer` but instead of clamping it repeats */ | ||||
| /* returns true if value was cycled */ | ||||
| bool repeat_ftimer(float *value, float at); | ||||
| TWN_API bool repeat_ftimer(float *value, float at); | ||||
|  | ||||
| /* http://www.azillionmonkeys.com/qed/sqroot.html */ | ||||
| static inline float fast_sqrt(float x) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user