windows build compatibility, twn_game_object as abstraction for bridging game code and the engine
This commit is contained in:
		
							
								
								
									
										104
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								CMakeLists.txt
									
									
									
									
									
								
							| @@ -2,10 +2,6 @@ cmake_minimum_required(VERSION 3.21) | |||||||
|  |  | ||||||
| project(townengine LANGUAGES C) | project(townengine LANGUAGES C) | ||||||
|  |  | ||||||
| list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) |  | ||||||
|  |  | ||||||
| set(CMAKE_EXPORT_COMPILE_COMMANDS ON) |  | ||||||
|  |  | ||||||
| # dependencies | # dependencies | ||||||
| find_package(SDL2 REQUIRED GLOBAL) | find_package(SDL2 REQUIRED GLOBAL) | ||||||
| find_package(SDL2_image REQUIRED GLOBAL) | find_package(SDL2_image REQUIRED GLOBAL) | ||||||
| @@ -15,14 +11,14 @@ if (NOT CMAKE_BUILD_TYPE) | |||||||
|         set(CMAKE_BUILD_TYPE Debug) |         set(CMAKE_BUILD_TYPE Debug) | ||||||
| endif () | endif () | ||||||
|  |  | ||||||
| set(TOWNENGINE_TARGET townengine CACHE INTERNAL "") | set(TWN_TARGET townengine CACHE INTERNAL "") | ||||||
| set(TOWNENGINE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") | set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") | ||||||
|  |  | ||||||
| option(TOWNENGINE_HOT_RELOAD "Enable hot reloading support" ON) | option(TWN_FEATURE_DYNLIB_GAME  "Enable dynamic library loading support" ON) | ||||||
| option(TOWNENGINE_ARCHIVE_DATA "Enable archival of assets" OFF) | option(TWN_ARCHIVE_DATA         "Enable archival of assets"              OFF) | ||||||
|  |  | ||||||
| # add -fPIC globally so that it's linked well | # 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_BUILD_SHARED FALSE) | ||||||
| set(PHYSFS_DISABLE_INSTALL TRUE) | set(PHYSFS_DISABLE_INSTALL TRUE) | ||||||
| @@ -45,12 +41,13 @@ else() | |||||||
|         set(SYSTEM_SOURCE_FILES) |         set(SYSTEM_SOURCE_FILES) | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
| set(TOWNENGINE_SOURCE_FILES | set(TWN_SOURCE_FILES | ||||||
|         third-party/physfs/extras/physfsrwops.c |         third-party/physfs/extras/physfsrwops.c | ||||||
|         third-party/stb/stb_vorbis.c |         third-party/stb/stb_vorbis.c | ||||||
|         third-party/glad/src/glad.c |         third-party/glad/src/glad.c | ||||||
|  |  | ||||||
|         townengine/main.c |         townengine/twn_loop.c | ||||||
|  |         townengine/twn_main.c | ||||||
|         townengine/config.h |         townengine/config.h | ||||||
|         townengine/context/context.c townengine/context.h |         townengine/context/context.c townengine/context.h | ||||||
|         townengine/audio/audio.c townengine/audio.h |         townengine/audio/audio.c townengine/audio.h | ||||||
| @@ -59,25 +56,29 @@ set(TOWNENGINE_SOURCE_FILES | |||||||
|         townengine/input/input.c townengine/input.h |         townengine/input/input.c townengine/input.h | ||||||
|         townengine/camera.c townengine/camera.h |         townengine/camera.c townengine/camera.h | ||||||
|         townengine/textures/textures.c |         townengine/textures/textures.c | ||||||
|  |         townengine/twn_game_object.c | ||||||
|  |  | ||||||
|  |         $<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:townengine/twn_main.c> | ||||||
|  |  | ||||||
|         ${SYSTEM_SOURCE_FILES}) |         ${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 | # base engine code, reused for games and tools | ||||||
| if (TOWNENGINE_HOT_RELOAD) | if (TWN_FEATURE_DYNLIB_GAME) | ||||||
|         add_library(${TOWNENGINE_TARGET} SHARED ${TOWNENGINE_SOURCE_FILES}) |         add_library(${TWN_TARGET} SHARED ${TWN_SOURCE_FILES}) | ||||||
| else () | else () | ||||||
|         add_library(${TOWNENGINE_TARGET} STATIC ${TOWNENGINE_SOURCE_FILES}) |         add_library(${TWN_TARGET} STATIC ${TWN_SOURCE_FILES}) | ||||||
| endif () | 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 11 | ||||||
|                                            C_STANDARD_REQUIRED ON |                                            C_STANDARD_REQUIRED ON | ||||||
|                                            C_EXTENSIONS ON) # extensions are required by stb_ds.h |                                            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/glad/include/glad/glad.h | ||||||
|         third-party/stb/stb_ds.h) |         third-party/stb/stb_ds.h) | ||||||
|  |  | ||||||
| @@ -163,7 +164,7 @@ function(give_options target) | |||||||
|                                              ORGANIZATION_NAME="${ORGANIZATION_NAME}" |                                              ORGANIZATION_NAME="${ORGANIZATION_NAME}" | ||||||
|                                              APP_NAME="${APP_NAME}" |                                              APP_NAME="${APP_NAME}" | ||||||
|                                              PACKAGE_EXTENSION="${PACKAGE_EXTENSION}" |                                              PACKAGE_EXTENSION="${PACKAGE_EXTENSION}" | ||||||
|                                              $<$<BOOL:${TOWNENGINE_HOT_RELOAD}>:HOT_RELOAD_SUPPORT>) |                                              $<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>) | ||||||
| endfunction() | endfunction() | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -178,18 +179,17 @@ function(include_deps target) | |||||||
|                 third-party/stb |                 third-party/stb | ||||||
|                 third-party/x-watcher) |                 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}) |         target_include_directories(${target} SYSTEM PRIVATE ${THIRD_PARTY_INCLUDES}) | ||||||
|  |  | ||||||
|         # allow access to headers from any point in source tree |         # 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() | endfunction() | ||||||
|  |  | ||||||
|  |  | ||||||
| function(link_deps target) | function(link_deps target) | ||||||
|         target_link_libraries(${target} PUBLIC |         target_link_libraries(${target} PUBLIC | ||||||
|                                         SDL2::SDL2 |                                         SDL2::SDL2 | ||||||
|                                         SDL2::SDL2main |  | ||||||
|                                         SDL2_image::SDL2_image |                                         SDL2_image::SDL2_image | ||||||
|                                         SDL2_ttf::SDL2_ttf |                                         SDL2_ttf::SDL2_ttf | ||||||
|                                         physfs-static |                                         physfs-static | ||||||
| @@ -198,21 +198,23 @@ endfunction() | |||||||
|  |  | ||||||
|  |  | ||||||
| function(use_townengine target sources output_directory data_dir) | function(use_townengine target sources output_directory data_dir) | ||||||
|         if (TOWNENGINE_HOT_RELOAD) |         if (TWN_FEATURE_DYNLIB_GAME) | ||||||
|                 # game shared library, for reloading |                 # game shared library, for reloading | ||||||
|                 add_library(${target}_game SHARED ${sources}) |                 add_library(${target}_game SHARED ${sources}) | ||||||
|                 set_target_properties(${target}_game PROPERTIES |  | ||||||
|                                                      OUTPUT_NAME game |  | ||||||
|                                                      LIBRARY_OUTPUT_DIRECTORY ${output_directory}) |  | ||||||
|                 give_options(${target}_game) |                 give_options(${target}_game) | ||||||
|                 include_deps(${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 |                 # 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 |                 # put libtownengine.so alongside the binary | ||||||
|                 set_target_properties(${TOWNENGINE_TARGET} PROPERTIES |                 set_target_properties(${TWN_TARGET} PROPERTIES | ||||||
|                                                     LIBRARY_OUTPUT_DIRECTORY ${output_directory}) |                                                     LIBRARY_OUTPUT_DIRECTORY ${output_directory}) | ||||||
|         else () |         else () | ||||||
|                 add_executable(${target}_app ${sources}) |                 add_executable(${target}_app ${sources}) | ||||||
| @@ -231,42 +233,51 @@ function(use_townengine target sources output_directory data_dir) | |||||||
|         give_options(${target}_app) |         give_options(${target}_app) | ||||||
|         include_deps(${target}_app) |         include_deps(${target}_app) | ||||||
|         link_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 |         set_target_properties(${target}_app PROPERTIES | ||||||
|                                             RUNTIME_OUTPUT_DIRECTORY ${output_directory}) |                                             RUNTIME_OUTPUT_DIRECTORY ${output_directory}) | ||||||
|  |  | ||||||
|         # copy dlls for baby windows |         # copy dlls for baby windows | ||||||
|         add_custom_command(TARGET ${target}_app POST_BUILD |         add_custom_command(TARGET ${target}_app POST_BUILD | ||||||
|                            COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:${target}_app> |                            COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:${target}_app> | ||||||
|                                                                $<TARGET_RUNTIME_DLLS:${target}_app> |                                                                          $<TARGET_FILE_DIR:${target}_app> | ||||||
|                            COMMAND_EXPAND_LISTS) |                            COMMAND_EXPAND_LISTS) | ||||||
|  |  | ||||||
|         if (UNIX) |         set(TWN_BOOTSTRAP_EXEC_ARGS | ||||||
|                 # create a bootstrapping script |                 "$<IF:$<BOOL:${TWN_ARCHIVE_DATA}>,--data-dir ./data.${PACKAGE_EXTENSION},--data-dir ${data_dir}>") | ||||||
|                 set(TOWNENGINE_BOOTSTRAP_EXEC_ARGS |  | ||||||
|                         "$<IF:$<BOOL:${TOWNENGINE_ARCHIVE_DATA}>,--data-dir ./data.${PACKAGE_EXTENSION},--data-dir ${data_dir}>") |  | ||||||
|  |  | ||||||
|                 string(JOIN "\n" TOWNENGINE_BOOTSTRAP |         string(JOIN "\n" TWN_BOOTSTRAP_SH | ||||||
|                          "#!/bin/env sh" |                          "#!/bin/env sh" | ||||||
|                          "cd \"$(dirname \"$0\")\"" |                          "cd \"$(dirname \"$0\")\"" | ||||||
|                                  "LD_LIBRARY_PATH=./ ./launcher ${TOWNENGINE_BOOTSTRAP_EXEC_ARGS}" |                          "LD_LIBRARY_PATH=./ ./launcher ${TWN_BOOTSTRAP_EXEC_ARGS}" | ||||||
|                          "") |                          "") | ||||||
|  |  | ||||||
|         FILE(GENERATE OUTPUT ${output_directory}/${target} |         FILE(GENERATE OUTPUT ${output_directory}/${target} | ||||||
|                      CONTENT "${TOWNENGINE_BOOTSTRAP}" |              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}.bat | ||||||
|  |                      CONTENT "${TWN_BOOTSTRAP_BAT}" | ||||||
|                      FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ) |                      FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ) | ||||||
|         endif () |         endif () | ||||||
|  |  | ||||||
|         if (TOWNENGINE_ARCHIVE_DATA) |         if (TWN_ARCHIVE_DATA) | ||||||
|                 # zip up assets |                 # zip up assets | ||||||
|                 add_custom_command(TARGET ${target}_app POST_BUILD |                 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}) |                                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) | ||||||
|         endif () |         endif () | ||||||
| endfunction() | endfunction() | ||||||
|  |  | ||||||
| give_options(${TOWNENGINE_TARGET}) | give_options(${TWN_TARGET}) | ||||||
| include_deps(${TOWNENGINE_TARGET}) | include_deps(${TWN_TARGET}) | ||||||
| link_deps(${TOWNENGINE_TARGET}) | link_deps(${TWN_TARGET}) | ||||||
|  |  | ||||||
| # build the testgame if this cmake list is built directly | # build the testgame if this cmake list is built directly | ||||||
| if (${CMAKE_PROJECT_NAME} MATCHES townengine) | if (${CMAKE_PROJECT_NAME} MATCHES townengine) | ||||||
| @@ -274,6 +285,7 @@ if (${CMAKE_PROJECT_NAME} MATCHES townengine) | |||||||
| endif () | endif () | ||||||
|  |  | ||||||
| # move compie_commands.json into root directory so that it plays nicer with editors | # 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 |                    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 "townengine/game_api.h" | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  |  | ||||||
| static void title_tick(struct state *state) { | static void title_tick(struct state *state) { | ||||||
|     struct scene_title *scn = (struct scene_title *)state->scene; |     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 */ |     /* 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); |     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"; |     const char *font = "fonts/kenney-pixel.ttf"; | ||||||
|     int text_h = 32; |     int text_h = 32; | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| #ifndef AUDIO_H | #ifndef AUDIO_H | ||||||
| #define AUDIO_H | #define AUDIO_H | ||||||
|  |  | ||||||
|  | #include "twn_engine_api.h" | ||||||
|  |  | ||||||
| #include <stdbool.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 */ | /* path must contain valid file extension to infer which file format it is */ | ||||||
| /* supported formats: .ogg, .xm */ | /* supported formats: .ogg, .xm */ | ||||||
| /* preserves args that are already specified on the channel */ | /* 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 */ | /* could be used for modifying args */ | ||||||
| /* warn: is only valid if no other calls to audio are made */ | /* 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 | #endif | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| #include "rendering/internal_api.h" | #include "rendering/internal_api.h" | ||||||
| #include "textures/internal_api.h" | #include "textures/internal_api.h" | ||||||
| #include "input.h" | #include "input.h" | ||||||
|  | #include "twn_engine_api.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
|  |  | ||||||
| @@ -65,6 +66,6 @@ typedef struct context { | |||||||
|     bool initialization_needed; |     bool initialization_needed; | ||||||
| } t_ctx; | } t_ctx; | ||||||
|  |  | ||||||
| extern t_ctx ctx; | TWN_API extern t_ctx ctx; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -8,15 +8,15 @@ | |||||||
| #include "townengine/audio.h" | #include "townengine/audio.h" | ||||||
| #include "townengine/util.h" | #include "townengine/util.h" | ||||||
| #include "townengine/input.h" | #include "townengine/input.h" | ||||||
|  | #include "twn_engine_api.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| #ifndef HOT_RELOAD_SUPPORT |  | ||||||
| /* sole game logic and display function. | /* sole game logic and display function. | ||||||
|    all state must be used from and saved to supplied state pointer. */ |    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. */ | /* called when application is closing. */ | ||||||
| extern void game_end(void); | TWN_API extern void game_end(void); | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #endif | #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 "config.h" | ||||||
| #include "vec.h" | #include "vec.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  | #include "twn_engine_api.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
|  |  | ||||||
| @@ -68,33 +69,33 @@ struct input_state { | |||||||
| }; | }; | ||||||
|  |  | ||||||
|  |  | ||||||
| void input_state_init(struct input_state *input); | TWN_API void input_state_init(struct input_state *input); | ||||||
| void input_state_deinit(struct input_state *input); | TWN_API void input_state_deinit(struct input_state *input); | ||||||
| void input_state_update(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, |                                 char *action_name, | ||||||
|                                 SDL_Scancode scancode); |                                 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, |                                   char *action_name, | ||||||
|                                   SDL_Scancode scancode); |                                   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, |                              char *action_name, | ||||||
|                              uint8_t mouse_button); |                              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, |                                char *action_name, | ||||||
|                                uint8_t mouse_button); |                                uint8_t mouse_button); | ||||||
|  |  | ||||||
| void input_add_action(struct input_state *input, char *action_name); | TWN_API 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_delete_action(struct input_state *input, char *action_name); | ||||||
|  |  | ||||||
| bool input_is_action_pressed(struct input_state *input, char *action_name); | TWN_API 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); | TWN_API 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_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); | TWN_API void input_set_mouse_captured(struct input_state *input, bool value); | ||||||
| bool input_is_mouse_captured(struct input_state *input); | TWN_API bool input_is_mouse_captured(struct input_state *input); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -1,2 +0,0 @@ | |||||||
| /* cmake nonsense */ |  | ||||||
| struct a { int b; }; |  | ||||||
| @@ -4,6 +4,7 @@ | |||||||
| #include "util.h" | #include "util.h" | ||||||
| #include "macros/option.h" | #include "macros/option.h" | ||||||
| #include "camera.h" | #include "camera.h" | ||||||
|  | #include "twn_engine_api.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
|  |  | ||||||
| @@ -25,24 +26,24 @@ typedef struct push_sprite_args { | |||||||
| /* pushes a sprite onto the sprite render queue */ | /* pushes a sprite onto the sprite render queue */ | ||||||
| /* this is a simplified version of push_sprite_ex for the most common case. */ | /* 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 */ | /* 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__})) | #define m_sprite(...) (push_sprite((t_push_sprite_args){__VA_ARGS__})) | ||||||
|  |  | ||||||
| /* pushes a filled rectangle onto the rectangle render queue */ | /* 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 */ | /* 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); | TWN_API void text_cache_init(struct text_cache *cache); | ||||||
| void text_cache_deinit(struct text_cache *cache); | TWN_API 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); | TWN_API 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 int get_text_width(char *string, int height_px, const char *font_path); | ||||||
|  |  | ||||||
| /* pushes a textured 3d triangle onto the render queue */ | /* pushes a textured 3d triangle onto the render queue */ | ||||||
| /* vertices are in absolute coordinates, relative to world origin */ | /* vertices are in absolute coordinates, relative to world origin */ | ||||||
| /* texture coordinates are in pixels */ | /* texture coordinates are in pixels */ | ||||||
| void unfurl_triangle(const char *path, | TWN_API void unfurl_triangle(const char *path, | ||||||
|                      t_fvec3 v0, |                      t_fvec3 v0, | ||||||
|                      t_fvec3 v1, |                      t_fvec3 v1, | ||||||
|                      t_fvec3 v2, |                      t_fvec3 v2, | ||||||
| @@ -70,6 +71,6 @@ void unfurl_triangle(const char *path, | |||||||
| //                      t_frect uvs); | //                      t_frect uvs); | ||||||
|  |  | ||||||
| /* pushes a camera state to be used for all future unfurl_* commands */ | /* 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 | #endif | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
|  |  | ||||||
| #include "../util.h" | #include "../util.h" | ||||||
| #include "../textures/modes.h" | #include "../textures/modes.h" | ||||||
|  | #include "../twn_engine_api.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
| #include <stb_rect_pack.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 */ | /* tests whether given key structure corresponds to any texture */ | ||||||
| #define m_texture_key_is_valid(p_key) ((p_key).id != (uint16_t)-1) | #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); | TWN_API void textures_cache_init(struct texture_cache *cache, SDL_Window *window); | ||||||
| void textures_cache_deinit(struct texture_cache *cache); | TWN_API void textures_cache_deinit(struct texture_cache *cache); | ||||||
|  |  | ||||||
| /* for debugging */ | /* 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. */ | /* 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 */ | /* 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 */ | /* repacks the current texture atlas based on the texture cache if needed */ | ||||||
| /* any previously returned srcrect results are invalidated after that */ | /* any previously returned srcrect results are invalidated after that */ | ||||||
| /* call it every time before rendering */ | /* 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 */ | /* returns a persistent handle to some texture in cache, loading it if needed */ | ||||||
| /* check the result with m_texture_key_is_valid() */ | /* 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 */ | /* 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) */ | /* 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 */ | /* 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 */ | /* 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 */ | /* 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 */ | /* 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 */ | /* 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 | #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/context.h" | ||||||
| #include "townengine/rendering.h" | #include "townengine/rendering.h" | ||||||
| #include "townengine/input/internal_api.h" | #include "townengine/input/internal_api.h" | ||||||
| #include "townengine/util.h" | #include "townengine/util.h" | ||||||
| #include "townengine/game_api.h" | #include "townengine/twn_game_object.h" | ||||||
| #include "townengine/audio/internal_api.h" | #include "townengine/audio/internal_api.h" | ||||||
| #include "townengine/textures/internal_api.h" | #include "townengine/textures/internal_api.h" | ||||||
| #include "townengine/rendering/internal_api.h" | #include "townengine/rendering/internal_api.h" | ||||||
| @@ -20,30 +21,6 @@ | |||||||
| #include <tgmath.h> | #include <tgmath.h> | ||||||
| #include <limits.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) { | static void poll_events(void) { | ||||||
|     SDL_Event e; |     SDL_Event e; | ||||||
| @@ -192,7 +169,7 @@ static void main_loop(void) { | |||||||
| 
 | 
 | ||||||
|             input_state_update(&ctx.input); |             input_state_update(&ctx.input); | ||||||
| 
 | 
 | ||||||
|             game_tick(); |             game_object_tick(); | ||||||
| 
 | 
 | ||||||
|             ctx.frame_accumulator -= ctx.desired_frametime; |             ctx.frame_accumulator -= ctx.desired_frametime; | ||||||
|             ctx.tick_count = (ctx.tick_count % ULLONG_MAX) + 1; |             ctx.tick_count = (ctx.tick_count % ULLONG_MAX) + 1; | ||||||
| @@ -375,8 +352,6 @@ static bool initialize(void) { | |||||||
|     } |     } | ||||||
|     */ |     */ | ||||||
| 
 | 
 | ||||||
|     game_object_mutex = SDL_CreateMutex(); |  | ||||||
| 
 |  | ||||||
|     return true; |     return true; | ||||||
| 
 | 
 | ||||||
| fail: | fail: | ||||||
| @@ -404,8 +379,6 @@ static void clean_up(void) { | |||||||
|     text_cache_deinit(&ctx.text_cache); |     text_cache_deinit(&ctx.text_cache); | ||||||
|     textures_cache_deinit(&ctx.texture_cache); |     textures_cache_deinit(&ctx.texture_cache); | ||||||
| 
 | 
 | ||||||
|     SDL_DestroyMutex(game_object_mutex); |  | ||||||
| 
 |  | ||||||
|     PHYSFS_deinit(); |     PHYSFS_deinit(); | ||||||
|     TTF_Quit(); |     TTF_Quit(); | ||||||
|     IMG_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) { | static void reset_state(void) { | ||||||
|     input_reset_state(&ctx.input); |     input_reset_state(&ctx.input); | ||||||
|     textures_reset_state(); |     textures_reset_state(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| int main(int argc, char **argv) { | int enter_loop(int argc, char **argv) { | ||||||
|     ctx.argc = argc; |     ctx.argc = argc; | ||||||
|     ctx.argv = argv; |     ctx.argv = argv; | ||||||
| 
 | 
 | ||||||
| @@ -537,26 +409,21 @@ int main(int argc, char **argv) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     load_game(); |     game_object_load(); | ||||||
| 
 | 
 | ||||||
|     ctx.was_successful = true; |     ctx.was_successful = true; | ||||||
|     ctx.initialization_needed = true; |     ctx.initialization_needed = true; | ||||||
| 
 | 
 | ||||||
|     while (ctx.is_running) { |     while (ctx.is_running) { | ||||||
|         /* only load the modified library after some time, as compilers make a lot of modifications */ |         if (game_object_try_reloading()) { | ||||||
|         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; |  | ||||||
|             ctx.initialization_needed = true; |             ctx.initialization_needed = true; | ||||||
|             reset_state(); |             reset_state(); | ||||||
|         } |         } | ||||||
|         SDL_UnlockMutex(game_object_mutex); |  | ||||||
| 
 | 
 | ||||||
|         main_loop(); |         main_loop(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     game_end(); |     game_object_unload(); | ||||||
| 
 | 
 | ||||||
|     clean_up(); |     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 | #define UTIL_H | ||||||
|  |  | ||||||
| #include "townengine/vec.h" | #include "townengine/vec.h" | ||||||
|  | #include "twn_engine_api.h" | ||||||
|  |  | ||||||
| #include <SDL2/SDL.h> | #include <SDL2/SDL.h> | ||||||
| #include <physfs.h> | #include <physfs.h> | ||||||
| @@ -17,28 +18,28 @@ | |||||||
| /* GENERAL UTILITIES */ | /* 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(title, text) cry_impl(__FILE__, __LINE__, title, text) | ||||||
| #define CRY_SDL(title) cry_impl(__FILE__, __LINE__, title, SDL_GetError()) | #define CRY_SDL(title) cry_impl(__FILE__, __LINE__, title, SDL_GetError()) | ||||||
| #define CRY_PHYSFS(title)                                               \ | #define CRY_PHYSFS(title)                                               \ | ||||||
| cry_impl(__FILE__, __LINE__, title, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())) | cry_impl(__FILE__, __LINE__, title, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())) | ||||||
|  |  | ||||||
|  |  | ||||||
| void log_info(const char *restrict format, ...); | TWN_API void log_info(const char *restrict format, ...); | ||||||
| void log_critical(const char *restrict format, ...); | TWN_API void log_critical(const char *restrict format, ...); | ||||||
| void log_warn(const char *restrict format, ...); | TWN_API void log_warn(const char *restrict format, ...); | ||||||
|  |  | ||||||
|  |  | ||||||
| /* for when there's absolutely no way to continue */ | /* 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. */ | /* "critical" allocation functions which will log and abort() on failure. */ | ||||||
| /* if it is reasonable to handle this gracefully, use the standard versions. */ | /* if it is reasonable to handle this gracefully, use the standard versions. */ | ||||||
| /* the stb implementations will be configured to use these */ | /* the stb implementations will be configured to use these */ | ||||||
| void *cmalloc(size_t size); | TWN_API void *cmalloc(size_t size); | ||||||
| void *crealloc(void *ptr, size_t size); | TWN_API void *crealloc(void *ptr, size_t size); | ||||||
| void *ccalloc(size_t num, size_t size); | TWN_API void *ccalloc(size_t num, size_t size); | ||||||
|  |  | ||||||
|  |  | ||||||
| /* DON'T FORGET ABOUT DOUBLE EVALUATION */ | /* DON'T FORGET ABOUT DOUBLE EVALUATION */ | ||||||
| @@ -58,20 +59,20 @@ void *ccalloc(size_t num, size_t size); | |||||||
| #define RAD2DEG (180 / M_PI) | #define RAD2DEG (180 / M_PI) | ||||||
|  |  | ||||||
| /* TODO: this is why generics were invented. sorry, i'm tired today */ | /* TODO: this is why generics were invented. sorry, i'm tired today */ | ||||||
| double clamp(double d, double min, double max); | TWN_API double clamp(double d, double min, double max); | ||||||
| float clampf(float f, float min, float max); | TWN_API float clampf(float f, float min, float max); | ||||||
| int clampi(int i, int min, int 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. */ | /* sets buf_out to a pointer to a byte buffer which must be freed. */ | ||||||
| /* returns the size of this buffer. */ | /* 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 */ | /* 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 */ | /* 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; | } 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)*/ | /* 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 { | typedef struct matrix4 { | ||||||
| @@ -129,16 +130,16 @@ typedef struct matrix4 { | |||||||
|  * example: |  * example: | ||||||
|  * tick_timer(&player->jump_air_timer); |  * 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 */ | /* decrements a floating point second-based timer, stopping at 0.0 */ | ||||||
| /* meant for poll based real time logic in game logic */ | /* meant for poll based real time logic in game logic */ | ||||||
| /* note that it should be decremented only on the next tick after its creation */ | /* 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 */ | /* same as `tick_ftimer` but instead of clamping it repeats */ | ||||||
| /* returns true if value was cycled */ | /* 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 */ | /* http://www.azillionmonkeys.com/qed/sqroot.html */ | ||||||
| static inline float fast_sqrt(float x) | static inline float fast_sqrt(float x) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user