diff options
Diffstat (limited to 'engine/core')
-rw-r--r-- | engine/core/abort.c | 93 | ||||
-rw-r--r-- | engine/core/alloc.c | 178 | ||||
-rw-r--r-- | engine/core/callbacks.c | 27 | ||||
-rw-r--r-- | engine/core/init.c | 48 | ||||
-rw-r--r-- | engine/core/logging.c | 114 | ||||
-rw-r--r-- | engine/core/mod.c | 90 | ||||
-rw-r--r-- | engine/core/thread.c | 218 |
7 files changed, 768 insertions, 0 deletions
diff --git a/engine/core/abort.c b/engine/core/abort.c new file mode 100644 index 0000000..5ee42f6 --- /dev/null +++ b/engine/core/abort.c @@ -0,0 +1,93 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman <dholman@gymli.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <rune/core/abort.h> +#include <rune/core/alloc.h> +#include <rune/core/init.h> +#include <rune/core/logging.h> +#include <rune/util/exits.h> +#include <stdlib.h> + +#define MAX_TRACE_ITEMS 30 + +#ifdef _WIN32 + +#include <windows.h> +#include <dbghelp.h> + +void _stack_trace(void) { + void* buffer[MAX_TRACE_ITEMS]; + HANDLE process = GetCurrentProcess(); + + SymInitialize(process, NULL, TRUE); + int num_links = CaptureStackBackTrace(0, MAX_TRACE_ITEMS, buffer, NULL); + SYMBOL_INFO *symbol = (SYMBOL_INFO*)rune_alloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char)); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = 255; + + for (int i = 0; i < num_links; i++) { + SymFromAddr(process, (DWORD64)(buffer[i]), 0, symbol); + log_output(LOG_INFO, "#%d: %s", i, symbol->Name, symbol->Address); + } + + rune_free(symbol); + SymCleanup(process); +} + +#else + +#include <execinfo.h> + +void _stack_trace(void) { + void* buffer[MAX_TRACE_ITEMS]; + int num_links = backtrace(buffer, MAX_TRACE_ITEMS); + char** symbuf = backtrace_symbols(buffer, num_links); + + for (int i = 0; i < num_links; i++) + log_output(LOG_INFO, "#%d: %s", i, symbuf[i]); + + free(symbuf); +} + +#endif + +NORET void rune_abort(void) { + log_output(LOG_INFO, "Abort called, printing stack trace"); + _stack_trace(); + rune_exit(); + exit(REXIT_FAIL); +} + +#ifdef MSVC + +NORET void __security_error_handler(void) { + log_output(LOG_FATAL, "Stack smashing detected in engine code"); + rune_abort(); +} + +#else + +NORET void __stack_chk_fail(void) { + log_output(LOG_FATAL, "Stack smashing detected in engine code"); + rune_abort(); +} + +#endif diff --git a/engine/core/alloc.c b/engine/core/alloc.c new file mode 100644 index 0000000..b75f1d3 --- /dev/null +++ b/engine/core/alloc.c @@ -0,0 +1,178 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman <dholman@gymli.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <rune/core/alloc.h> +#include <rune/core/logging.h> +#include <rune/core/profiling.h> +#include <stdlib.h> +#include <string.h> + +// TODO: implement block coalescing so we can reuse freed blocks + +#define DEADBLOCK ((void*)0xDEADBEEF) + +static mem_block_t first_block; + +static mem_block_t* _find_free_block(size_t sz) { + list_head_t *temp = &first_block.list; + mem_block_t *block; + while (temp != NULL) { + block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); + if (block->sz == sz && block->free == 1) + return block; + temp = temp->next; + } + return NULL; +} + +static mem_block_t* _find_block(void *ptr) { + list_head_t *temp = &first_block.list; + mem_block_t *block; + while (temp != NULL) { + block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); + if (block->ptr == ptr) + return block; + temp = temp->next; + } + return NULL; +} + +static mem_block_t* _alloc_block(size_t sz) { + RUNE_PROFILE_SCOPE("Block allocation"); + if (first_block.ptr == NULL) { + first_block.ptr = DEADBLOCK; + first_block.sz = 0; + } + + mem_block_t *ret = _find_free_block(sz); + if (ret != NULL) { + ret->free = 0; + RUNE_PROFILE_END(); + return ret->ptr; + } + + ret = malloc(sizeof(mem_block_t)); + if (ret == NULL) { + RUNE_PROFILE_END(); + log_output(LOG_ERROR, "Cannot allocate block of size %d", sz); + return NULL; + } + + ret->ptr = malloc(sz); + ret->sz = sz; + ret->free = 0; + list_add(&ret->list, &first_block.list); + RUNE_PROFILE_END(); + log_output(LOG_DEBUG, "Alloc'd block of size %d", sz); + return ret; +} + +static void _free_block(mem_block_t *block, int hard) { + RUNE_PROFILE_SCOPE("Block free"); + if (hard == 1) { + list_del(&block->list); + RUNE_PROFILE_END(); + free(block); + log_output(LOG_DEBUG, "Freed block of size %d", block->sz); + return; + } + block->free = 1; +} + +void* rune_alloc(size_t sz) { + if (sz == 0) + return NULL; + + RUNE_PROFILE_SCOPE("Pool allocation"); + mem_block_t *block = _find_free_block(sz); + if (block != NULL) { + block->free = 0; + RUNE_PROFILE_END(); + return block->ptr; + } + + block = _alloc_block(sz); + RUNE_PROFILE_END(); + return block->ptr; +} + +void* rune_calloc(size_t nmemb, size_t sz) { + if (sz == 0) + return NULL; + + RUNE_PROFILE_SCOPE("Zero array pool allocation"); + mem_block_t *block = _find_free_block(sz); + if (block != NULL) { + block->free = 0; + RUNE_PROFILE_END(); + return block->ptr; + } + + block = _alloc_block(sz); + memset(block->ptr, 0, sz); + RUNE_PROFILE_END(); + return block->ptr; +} + +void* rune_realloc(void *ptr, size_t sz) { + if (ptr == NULL || sz == 0) + return rune_alloc(sz); + + RUNE_PROFILE_SCOPE("Pool reallocation"); + mem_block_t *old = _find_block(ptr); + mem_block_t *new = _alloc_block(sz); + memcpy(new->ptr, old->ptr, old->sz); + old->free = 1; + RUNE_PROFILE_END(); + return new->ptr; +} + +void rune_free(void *ptr) { + if (ptr == NULL) + return; + + RUNE_PROFILE_SCOPE("Pool free"); + mem_block_t *block = _find_block(ptr); + if (block->free == 1) { + RUNE_PROFILE_END(); + return; + } + block->free = 1; + RUNE_PROFILE_END(); +} + +void rune_free_all(void) { + RUNE_PROFILE_SCOPE("Pool free all"); + list_head_t *temp = &first_block.list; + mem_block_t *block; + while (temp != NULL) { + block = (mem_block_t*)((void*)temp - offsetof(mem_block_t, list)); + if (block->ptr == DEADBLOCK) { + temp = temp->next; + continue; + } + + temp = temp->next; + if (block->ptr != NULL) + _free_block(block, 1); + } + RUNE_PROFILE_END(); +} diff --git a/engine/core/callbacks.c b/engine/core/callbacks.c new file mode 100644 index 0000000..ea7f50e --- /dev/null +++ b/engine/core/callbacks.c @@ -0,0 +1,27 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman <dholman@gymli.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <rune/core/callbacks.h> +#include <rune/core/logging.h> + +void error_callback(int error, const char *desc) { + log_output(LOG_ERROR, "%d: %s\n", error, desc); +} diff --git a/engine/core/init.c b/engine/core/init.c new file mode 100644 index 0000000..6fcd086 --- /dev/null +++ b/engine/core/init.c @@ -0,0 +1,48 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman <dholman@gymli.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <rune/core/init.h> +#include <rune/core/abort.h> +#include <rune/core/alloc.h> +#include <rune/core/config.h> +#include <rune/core/logging.h> +#include <rune/core/thread.h> +#include <rune/core/mod.h> +#include <rune/core/object.h> + +int rune_init(int argc, char* argv[]) { + log_output(LOG_INFO, "Started Rune Engine version %s", RUNE_VER); + + rune_init_default_settings(); + rune_init_thread_api(); + + rune_load_mods(); + rune_init_mods(); + + return 0; +} + +void rune_exit(void) { + log_output(LOG_INFO, "Engine shutdown requested"); + rune_clear_objs(); + rune_close_mods(); + rune_free_all(); +} diff --git a/engine/core/logging.c b/engine/core/logging.c new file mode 100644 index 0000000..b7f77bd --- /dev/null +++ b/engine/core/logging.c @@ -0,0 +1,114 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman <dholman@gymli.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <rune/core/logging.h> +#include <rune/core/config.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> + +#define COLOR_NONE "" +#define COLOR_DEFAULT "\033[0m" +#define COLOR_RED "\033[31m" +#define COLOR_GREEN "\033[32m" +#define COLOR_YELLOW "\033[33m" +#define COLOR_BLUE "\033[34m" + +#define LSTR_FATAL "[FATAL]" +#define LSTR_ERROR "[ERROR]" +#define LSTR_WARN "[WARNING]" +#define LSTR_INFO "[INFO]" +#define LSTR_DEBUG "[DEBUG]" + +static int debug_enabled = 0; +static int color_enabled = 0; + +void log_output(int level, const char *fmt, ...) { + char out[4096]; + memset(out, 0, sizeof(out)); + + debug_enabled = rune_get_log_debug(); + color_enabled = rune_get_log_color(); + + va_list arg_ptr; + va_start(arg_ptr, fmt); + vsnprintf(out, 4096, fmt, arg_ptr); + va_end(arg_ptr); + + char *lvl_str; + char *color = COLOR_NONE; + switch (level) { + case LOG_FATAL: + if (color_enabled == 1) + color = COLOR_RED; + lvl_str = LSTR_FATAL; + break; + case LOG_ERROR: + if (color_enabled == 1) + color = COLOR_RED; + lvl_str = LSTR_ERROR; + break; + case LOG_WARN: + if (color_enabled == 1) + color = COLOR_YELLOW; + lvl_str = LSTR_WARN; + break; + case LOG_INFO: + lvl_str = LSTR_INFO; + break; + case LOG_DEBUG: + if (color_enabled == 1) + color = COLOR_GREEN; + if (debug_enabled == 0) + return; + lvl_str = LSTR_DEBUG; + break; + } + + if (color_enabled == 0) { + printf("%s %s\n", lvl_str, out); + } else { + printf("%s%s %s\n", color, lvl_str, out); + printf(COLOR_DEFAULT); + } +} + +//#ifdef _WIN32 +// +//#include <windows.h> +// +//void enable_log_color(void) { +// HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); +// SetConsoleMode(handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING); +// color_enabled = 1; +//} +// +//#else +// +//void enable_log_color(void) { +// color_enabled = 1; +//} +// +//#endif +// +//void disable_log_color(void) { +// color_enabled = 0; +//} diff --git a/engine/core/mod.c b/engine/core/mod.c new file mode 100644 index 0000000..3d9e710 --- /dev/null +++ b/engine/core/mod.c @@ -0,0 +1,90 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman <dholman@gymli.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <rune/core/mod.h> +#include <rune/core/logging.h> +#include <rune/core/alloc.h> +#include <stdio.h> +#include <string.h> +#include <dirent.h> +#include <dlfcn.h> + +list_head_t *mods = NULL; + +void _load_mod(const char *filename) { + char mod_path[4096]; + sprintf(mod_path, "mods/%s", filename); + void *handle = dlopen(mod_path, RTLD_LAZY); + if (handle == NULL) + log_output(LOG_ERROR, "Error loading mod %s: %s", filename, dlerror()); +} + +void rune_load_mods(void) { + DIR *dir = opendir("mods"); + if (dir == NULL) { + log_output(LOG_INFO, "No mods folder found, skipping mod loading"); + return; + } + + struct dirent *mods_de; + while ((mods_de = readdir(dir)) != NULL) { + if (strstr(mods_de->d_name, ".so")) + _load_mod(mods_de->d_name); + } +} + +void rune_init_mods(void) { + list_head_t *temp = mods; + struct mod *mod; + while (temp != NULL) { + mod = (struct mod*)((void*)temp - offsetof(struct mod, list)); + (*mod->init_func)(); + temp = temp->next; + } +} + +void rune_close_mods(void) { + if (mods == NULL) + return; + + list_head_t *temp = mods; + struct mod *mod; + while (temp != NULL) { + mod = (struct mod*)((void*)temp - offsetof(struct mod, list)); + (*mod->exit_func)(); + temp = temp->next; + rune_free(mod); + } +} + +void rune_register_mod(const char *name, mod_func init_func, mod_func exit_func, mod_func update_func) { + struct mod *new = rune_alloc(sizeof(struct mod)); + new->name = name; + new->init_func = init_func; + new->exit_func = exit_func; + new->update_func = update_func; + new->list.next = NULL; + new->list.prev = NULL; + if (mods == NULL) + mods = &new->list; + else + list_add(&new->list, mods); +} diff --git a/engine/core/thread.c b/engine/core/thread.c new file mode 100644 index 0000000..3c5db8c --- /dev/null +++ b/engine/core/thread.c @@ -0,0 +1,218 @@ +/* + * Rune Game Engine + * Copyright 2024 Danny Holman <dholman@gymli.org> + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include <rune/core/thread.h> +#include <rune/core/logging.h> +#include <rune/core/alloc.h> +#include <pthread.h> +#include <string.h> +#include <stdatomic.h> + +static list_head_t *threads = NULL; +static list_head_t *mutexes = NULL; +static int next_tid = 0; +static int next_mid = 0; + +struct start_args { + struct thread *thread; + void* (*thread_fn)(void*); + void *thread_args; +}; + +static struct thread* _find_thread_by_handle(void *handle) { + if (threads == NULL) + return NULL; + + list_head_t *temp = threads; + struct thread *ret; + while (temp != NULL) { + ret = (struct thread*)((void*)temp - offsetof(struct thread, list)); + if (pthread_equal(*(pthread_t*)ret->thread_handle, *(pthread_t*)handle) == 1) + return ret; + temp = temp->next; + } + return NULL; +} + +static struct thread* _find_thread_by_id(int ID) { + if (threads == NULL) + return NULL; + + list_head_t *temp = threads; + struct thread *ret; + while (temp != NULL) { + ret = (struct thread*)((void*)temp - offsetof(struct thread, list)); + if (ret->ID == ID) + return ret; + temp = temp->next; + } + return NULL; +} + +static struct mutex* _find_mutex_by_id(int ID) { + if (mutexes == NULL) + return NULL; + + list_head_t *temp = mutexes; + struct mutex *ret; + while (temp != NULL) { + ret = (struct mutex*)((void*)temp - offsetof(struct mutex, list)); + if (ret->ID == ID) + return ret; + temp = temp->next; + } + return NULL; +} + + +static void _cleanup_pthread(void *arg) { + struct thread *thread = (struct thread*)arg; + if (&thread->list != threads) + list_del(&thread->list); + rune_free(thread->thread_handle); + rune_free(thread); +} + +static void* _startup_pthread(void *arg) { + struct start_args *start_args = (struct start_args*)arg; + + void* (*thread_fn)(void *data) = start_args->thread_fn; + pthread_cleanup_push(_cleanup_pthread, start_args->thread); + if (thread_fn != NULL) + (*thread_fn)(start_args->thread_args); + pthread_cleanup_pop(1); + return NULL; +} + +void rune_init_thread_api(void) { + struct thread *start_thread = rune_alloc(sizeof(struct thread)); + start_thread->ID = next_tid++; + start_thread->detached = 0; + start_thread->thread_handle = rune_alloc(sizeof(pthread_t)); + *(pthread_t*)start_thread->thread_handle = pthread_self(); + pthread_cleanup_push(_cleanup_pthread, threads); + pthread_cleanup_pop(0); +} + +int rune_thread_init(void* (*thread_fn)(void *data), void *data, int detached) { + struct thread *thread = rune_alloc(sizeof(struct thread)); + thread->ID = next_tid++; + thread->detached = detached; + thread->thread_handle = rune_alloc(sizeof(pthread_t)); + if (threads == NULL) + threads = &thread->list; + else + list_add(&thread->list, threads); + + struct start_args *args = rune_alloc(sizeof(struct start_args)); + args->thread = thread; + args->thread_fn = thread_fn; + args->thread_args = data; + int retval = pthread_create(thread->thread_handle, NULL, _startup_pthread, args); + rune_free(args); + if (retval != 0) { + log_output(LOG_ERROR, "Thread creation failed: %s", strerror(retval)); + return -1; + } + log_output(LOG_DEBUG, "Initialized new thread with ID=%d", thread->ID); + if (detached == 1) { + log_output(LOG_DEBUG, "Thread %d has been detached, join no longer possible"); + pthread_detach(*(pthread_t*)thread->thread_handle); + } + return thread->ID; +} + +int rune_thread_cancel(int ID) { + log_output(LOG_DEBUG, "Received cancel request for thread %d", ID); + struct thread *thread = _find_thread_by_id(ID); + if (thread == NULL) { + log_output(LOG_ERROR, "Thread %d does not exist", ID); + return -1; + } + pthread_cancel(*((pthread_t*)thread->thread_handle)); + return 0; +} + +int rune_thread_join(int ID, void **retval) { + struct thread *thread = _find_thread_by_id(ID); + if (thread == NULL) + return 0; + + if (thread->detached == 1) { + log_output(LOG_ERROR, "Cannot join thread %d, detached", thread->ID); + return -1; + } + pthread_join(*((pthread_t*)thread->thread_handle), retval); + return 0; +} + +int rune_thread_self(void) { + pthread_t cur = pthread_self(); + struct thread *ret = _find_thread_by_handle((void*)&cur); + if (ret != NULL) + return ret->ID; + return -1; +} + +void rune_thread_exit(void *retval) { + pthread_t cur = pthread_self(); + struct thread *self = _find_thread_by_handle((void*)&cur); + log_output(LOG_DEBUG, "Thread %d called thread_exit", self->ID); + pthread_exit(retval); +} + +int rune_mutex_init(void) { + struct mutex *mutex = rune_alloc(sizeof(struct mutex)); + mutex->ID = next_mid++; + mutex->mutex_handle = rune_alloc(sizeof(pthread_mutex_t)); + pthread_mutex_init((pthread_mutex_t*)mutex->mutex_handle, NULL); + if (mutexes == NULL) + mutexes = &mutex->list; + else + list_add(&mutex->list, mutexes); + return mutex->ID; +} + +int rune_mutex_destroy(int ID) { + struct mutex *mutex = _find_mutex_by_id(ID); + rune_free(mutex->mutex_handle); + if (&mutex->list != mutexes) + list_del(&mutex->list); + rune_free(mutex); +} + +int rune_mutex_lock(int ID) { + struct mutex *mutex = _find_mutex_by_id(ID); + int retval = pthread_mutex_lock((pthread_mutex_t*)mutex->mutex_handle); + if (retval != 0) { + char *str = strerror(retval); + log_output(LOG_ERROR, "Cannot lock mutex %d: %s", mutex->ID, str); + } +} + +int rune_mutex_unlock(int ID) { + struct mutex *mutex = _find_mutex_by_id(ID); + int retval = pthread_mutex_unlock((pthread_mutex_t*)mutex->mutex_handle); + if (retval != 0) { + char *str = strerror(retval); + log_output(LOG_ERROR, "Cannot unlock mutex %d: %s", mutex->ID, str); + } +} |