From 2e1eaf4c1c512761c7585267175bed420154172d Mon Sep 17 00:00:00 2001 From: Ulysse Cura Date: Fri, 24 Jul 2026 03:23:03 +0200 Subject: [PATCH] Moved ecs.c into core and added much better error and debug handling. --- src/Makefile | 8 +- src/asset_manager.c | 182 +++++++++++++++++++--------- src/display.c | 67 +++++++---- src/ecs.c | 189 ++++++++++++++++++++++++++++++ src/{event.c => events.c} | 2 +- src/headers/asset_manager.h | 10 +- src/headers/display.h | 12 +- src/headers/ecs.h | 78 ++++++++++++ src/headers/errors.h | 25 ++++ src/headers/{event.h => events.h} | 7 ++ src/headers/game.h | 46 ++++++++ src/headers/linked_list.h | 11 +- src/linked_list.c | 70 ++++++++--- src/main.c | 21 ++++ 14 files changed, 612 insertions(+), 116 deletions(-) create mode 100644 src/ecs.c rename src/{event.c => events.c} (92%) create mode 100644 src/headers/ecs.h create mode 100644 src/headers/errors.h rename src/headers/{event.h => events.h} (67%) create mode 100644 src/headers/game.h create mode 100644 src/main.c diff --git a/src/Makefile b/src/Makefile index ce79d2f..195b71e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,16 +2,18 @@ CORE_SOURCES := \ linked_list.c \ vector2d.c \ + events.c \ asset_manager.c \ + ecs.c \ display.c \ - event.c + main.c # Compiler and flags CORE_CC = gcc ifeq ($(RELEASE), 1) CORE_CFLAGS = --std=c17 --pedantic -O3 -DASSET_FILE_NAME=\"$(ASSET_OUTPUT)\" $(shell pkg-config --cflags sdl2) # RELEASE else -CORE_CFLAGS = --std=c17 --pedantic -O0 -g -DASSET_FILE_NAME=\"$(ASSET_OUTPUT)\" $(shell pkg-config --cflags sdl2) \ +CORE_CFLAGS = --std=c17 --pedantic -O0 -g -DDEBUG=1 -DASSET_FILE_NAME=\"$(ASSET_OUTPUT)\" $(shell pkg-config --cflags sdl2) \ -Wall -Wno-missing-braces -Wextra -Wno-missing-field-initializers \ -Wformat=2 -Wswitch-default -Wswitch-enum -Wcast-align \ -Wpointer-arith -Wbad-function-cast -Wstrict-overflow=5 \ @@ -21,7 +23,7 @@ CORE_CFLAGS = --std=c17 --pedantic -O0 -g -DASSET_FILE_NAME=\"$(ASSET_OUTPUT)\" -Wold-style-definition # DEVELOPEMENT endif -CORE_INCLUDE_DIRS = $(CORE_DIR)/headers +CORE_INCLUDE_DIRS = $(CORE_DIR)/headers $(SOURCE_DIR)/components/headers CORE_INCLUDE_DIRS := $(addprefix -I, $(CORE_INCLUDE_DIRS)) CORE_AR = ar diff --git a/src/asset_manager.c b/src/asset_manager.c index 5c97a8b..166a920 100644 --- a/src/asset_manager.c +++ b/src/asset_manager.c @@ -3,17 +3,22 @@ #include #include #include -#include -#include "linked_list.h" #include "memory_alloc.h" +#include "linked_list.h" #include "display.h" +#include "errors.h" asset_t *create_asset(asset_type_t asset_type) { asset_t *asset = malloc(sizeof(asset_t)); + if(!asset) + { + error_printf("Failed to allocate memory : %d", errno); + return NULL; + } asset->type = asset_type; - asset->nb_refs = 1; + asset->nb_refs = 0; return asset; } @@ -26,8 +31,10 @@ void destroy_asset(asset_t *asset) { case ASSET_TEXTURE: SDL_DestroyTexture(asset->data.texture); + break; default: + error_printf("Asset type not valid : %d", asset->type); break; } @@ -40,7 +47,7 @@ void destroy_asset_header_def(asset_header_def_t *asset_header_def) free(asset_header_def); } -void asset_manager_init(asset_manager_t *asset_manager) +int asset_manager_init(asset_manager_t *asset_manager) { linked_list_init(&asset_manager->assets, sizeof(asset_t), (deleter_t)destroy_asset); linked_list_init(&asset_manager->assets_header_defs, sizeof(asset_header_def_t), (deleter_t)destroy_asset_header_def); @@ -48,47 +55,59 @@ void asset_manager_init(asset_manager_t *asset_manager) asset_manager->asset_file = fopen(ASSET_FILE_NAME, "rb"); if(!asset_manager->asset_file) { - printf("can't open asset file : %s.\n", ASSET_FILE_NAME); - return; + error_printf("Failed to open asset file : %d", errno); + return EXIT_FAILURE; } size_t nb_assets; size_t ret = fread(&nb_assets, sizeof(nb_assets), 1, asset_manager->asset_file); if(ret != 1) { - printf("can't read asset file."); - return; + error_printf("Failed to read asset file."); + return EXIT_FAILURE; } for(size_t i = 0; i < nb_assets; i++) { elem_t *elem = elem_create(&asset_manager->assets_header_defs); + if(!elem) return EXIT_FAILURE; size_t cap = 0; ret = getdelim(&((asset_header_def_t *)elem->data)->name, &cap, '\0', asset_manager->asset_file); if(ret == SIZE_MAX) { - printf("can't read asset file."); - return; + error_printf("Failed to read asset file."); + return EXIT_FAILURE; } ret = fread(&((asset_header_def_t *)elem->data)->start, sizeof(((asset_header_def_t *)elem->data)->start), 1, asset_manager->asset_file); if(ret != 1) { - printf("can't read asset file."); - return; + error_printf("Failed to read asset file."); + return EXIT_FAILURE; } linked_list_push_back_elem(&asset_manager->assets_header_defs, elem); } + + debug_printf("Initialized asset manager."); + return EXIT_SUCCESS; } -void asset_manager_exit(asset_manager_t *asset_manager) +int asset_manager_exit(asset_manager_t *asset_manager) { linked_list_clear(&asset_manager->assets_header_defs); linked_list_clear(&asset_manager->assets); - fclose(asset_manager->asset_file); + + if(fclose(asset_manager->asset_file)) + { + error_printf("Failed to close asset file : %d", errno); + return EXIT_FAILURE; + } + + debug_printf("Exited asset manager."); + return EXIT_SUCCESS; } // Only for this file @@ -117,105 +136,148 @@ static inline bool condition_is_asset_header_def_name(elem_t *elem, va_list args return !strcmp(((asset_header_def_t *)elem->data)->name, va_arg(args, const char *)); } -static inline void load_texture(asset_manager_t *asset_manager, asset_t *asset, asset_header_def_t *asset_header_def) +static inline int load_texture(asset_manager_t *asset_manager, asset_t *asset, asset_header_def_t *asset_header_def) { if(fseek(asset_manager->asset_file, asset_header_def->start, SEEK_SET)) { - printf("Couldn't seek in asset file."); - return; + error_printf("Failed to seek in asset file : %d", errno); + return EXIT_FAILURE; } size_t width, height; if(fread(&width, sizeof(width), 1, asset_manager->asset_file) != 1) { - printf("Failed to read in asset file."); - return; + error_printf("Failed to read in asset file."); + return EXIT_FAILURE; } if(fread(&height, sizeof(height), 1, asset_manager->asset_file) != 1) { - printf("Failed to read in asset file."); - return; + error_printf("Failed to read in asset file."); + return EXIT_FAILURE; } SDL_Surface *tmp_surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 16, SDL_PIXELFORMAT_RGB565); + if(!tmp_surface) + { + error_printf("Failed to create temporary surface : %s", SDL_GetError()); + return EXIT_FAILURE; + } if(SDL_LockSurface(tmp_surface)) { - printf("Couldn't lock surface : %s\n", SDL_GetError()); - return; + printf("Failed to lock temporary surface : %s", SDL_GetError()); + SDL_FreeSurface(tmp_surface); + return EXIT_FAILURE; } if(fread(tmp_surface->pixels, sizeof(uint16_t), width * height, asset_manager->asset_file) != width * height) { printf("Failed to read asset file."); - return; + SDL_FreeSurface(tmp_surface); + return EXIT_FAILURE; } - SDL_SetColorKey(tmp_surface, SDL_TRUE, SDL_MapRGB(tmp_surface->format, 0xFF, 0x00, 0xFF)); + if(SDL_SetColorKey(tmp_surface, SDL_TRUE, SDL_MapRGB(tmp_surface->format, 0xFF, 0x00, 0xFF))) + { + error_printf("Failed to set color key of temporary surface : %s", SDL_GetError()); + SDL_FreeSurface(tmp_surface); + return EXIT_FAILURE; + } asset->data.texture = SDL_CreateTextureFromSurface(renderer, tmp_surface); + if(!asset->data.texture) + { + error_printf("Failed to create texture from temporary surface : %s", SDL_GetError()); + SDL_FreeSurface(tmp_surface); + return EXIT_FAILURE; + } SDL_FreeSurface(tmp_surface); + + return EXIT_SUCCESS; } -void asset_manager_load_asset(asset_manager_t *asset_manager, const char *asset_name, asset_type_t asset_type) +int asset_manager_load_asset(asset_manager_t *asset_manager, const char *asset_name, asset_type_t asset_type) { + // Check if asset has alread been loaded. asset_t *asset = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); - if(asset) + if(!asset) { - asset->nb_refs++; - return; + // If it didn't, load it + asset_header_def_t *asset_header_def = linked_list_get_if(&asset_manager->assets_header_defs, condition_is_asset_header_def_name, asset_name); + if(!asset_header_def) + { + error_printf("Asset doesn't exist in asset file : %s", asset_name); + return EXIT_FAILURE; + } + + asset = create_asset(asset_type); + if(!asset) return EXIT_FAILURE; + + asset->name = malloc(strlen(asset_name) + 1); + if(!asset->name) + { + error_printf("Failed to allocate memory for asset name : %d", errno); + destroy_asset(asset); + return EXIT_FAILURE; + } + strcpy(asset->name, asset_name); + asset->type = asset_type; + + switch(asset_type) + { + case ASSET_TEXTURE: + if(load_texture(asset_manager, asset, asset_header_def)) return EXIT_FAILURE; + break; + + default: + error_printf("Asset type doesn't exists : %d", asset_type); + destroy_asset(asset); + return EXIT_FAILURE; + } + + linked_list_push_back(&asset_manager->assets, asset); } - asset_header_def_t *asset_header_def = linked_list_get_if(&asset_manager->assets_header_defs, condition_is_asset_header_def_name, asset_name); - if(!asset_header_def) - { - printf("Asset doesn't exist in asset file : %s\n", asset_name); - return; - } - - asset = create_asset(asset_type); - asset->name = malloc(strlen(asset_name) + 1); - strcpy(asset->name, asset_name); - asset->type = asset_type; - - switch(asset_type) - { - case ASSET_TEXTURE: - load_texture(asset_manager, asset, asset_header_def); - break; - - default: - return; - } - - linked_list_push_back(&asset_manager->assets, asset); + asset->nb_refs++; + debug_printf("Asset \"%s\" has been loaded, nb refs : %lu", asset->name, asset->nb_refs); + return EXIT_SUCCESS; } -void asset_manager_let_go_asset(asset_manager_t *asset_manager, const char *asset_name) +int asset_manager_let_go_asset(asset_manager_t *asset_manager, const char *asset_name) { asset_t *asset = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); - if(asset) + if(!asset) { - asset->nb_refs--; + error_printf("Asset doesn't exists : %s", asset_name); } + + asset->nb_refs--; + debug_printf("Asset \"%s\" has been let go, nb refs : %lu", asset->name, asset->nb_refs); + return EXIT_SUCCESS; } texture_t *asset_manager_get_texture(asset_manager_t *asset_manager, const char *asset_name) { asset_t *asset = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); if(!asset) + { + error_printf("Asset \"%s\" hasn't been loaded yet.", asset_name); return NULL; + } if(asset->type != ASSET_TEXTURE) + { + error_printf("Asset \"%s\" isn't of type ASSET_TEXTURE.", asset_name); return NULL; + } return asset->data.texture; } -void asset_manager_draw_texture(texture_t *texture, rect_t *src_rect, rect_t *dst_rect, bool flip) +int asset_manager_draw_texture(texture_t *texture, rect_t *src_rect, rect_t *dst_rect, bool flip) { if(flip) { @@ -223,5 +285,11 @@ void asset_manager_draw_texture(texture_t *texture, rect_t *src_rect, rect_t *ds dst_rect->w = -dst_rect->w; } - SDL_RenderCopy(renderer, texture, src_rect, dst_rect); + if(SDL_RenderCopy(renderer, texture, src_rect, dst_rect)) + { + error_printf("Failed to copy texture to renderer : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; } diff --git a/src/display.c b/src/display.c index 9dc402d..8565d95 100644 --- a/src/display.c +++ b/src/display.c @@ -2,49 +2,70 @@ #include #include +#include "errors.h" SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; -inline void display_init(void) +inline int display_init(void) { - if(SDL_Init(SDL_INIT_EVERYTHING)) + if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { - printf("Couldn't initialize SDL: %s\n", SDL_GetError()); - return; + printf("SDL failed to initialised : %s\n", SDL_GetError()); + return EXIT_FAILURE; } window = SDL_CreateWindow("2D_Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DISPLAY_WIDTH * RENDER_SCALE, DISPLAY_HEIGHT * RENDER_SCALE, SDL_WINDOW_SHOWN); if(!window) { - printf("Couldn't create window : %s\n", SDL_GetError()); - return; + printf("Failed to create window : %s\n", SDL_GetError()); + return EXIT_FAILURE; } - renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if(!renderer) { - printf("Couldn't create renderer : %s\n", SDL_GetError()); - return; + error_printf("Failed to create renderer : %s\n", SDL_GetError()); + return EXIT_FAILURE; } - SDL_RenderSetScale(renderer, RENDER_SCALE, RENDER_SCALE); + if(SDL_RenderSetScale(renderer, RENDER_SCALE, RENDER_SCALE)) + { + error_printf("Failed to change renderer scale : %s\n", SDL_GetError()); + return EXIT_FAILURE; + } + + debug_printf("Initialized display."); + return EXIT_SUCCESS; +} + +inline void display_exit(void) +{ + if(renderer) SDL_DestroyRenderer(renderer); + if(window) SDL_DestroyWindow(window); + SDL_Quit(); + + debug_printf("Exited display."); +} + +inline int display_clear(void) +{ + if(SDL_SetRenderDrawColor(renderer, C_WHITE)) + { + error_printf("Failed to change renderer draw color : %s\n", SDL_GetError()); + return EXIT_FAILURE; + } + + if(SDL_RenderClear(renderer)) + { + error_printf("Failed to clear renderer : %s\n", SDL_GetError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; } inline void display_update(void) { SDL_RenderPresent(renderer); } - -inline void display_clear(void) -{ - SDL_SetRenderDrawColor(renderer, C_WHITE); - SDL_RenderClear(renderer); -} - -inline void display_exit(void) -{ - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); -} diff --git a/src/ecs.c b/src/ecs.c new file mode 100644 index 0000000..9eb08c3 --- /dev/null +++ b/src/ecs.c @@ -0,0 +1,189 @@ +#include +#include "memory_alloc.h" +#include "ecs.h" +#include "components.h" +#include "errors.h" + +component_t *create_component(component_type_t component_type) +{ + component_t *component = malloc(sizeof(component_t)); + if(!component) + { + error_printf("Failed to allocate memory : %d", errno); + return NULL; + } + + component->component_type = component_type; + + switch(component_type) + { +#define COMPONENT(NAME, PREFIX, DRAW) \ + case NAME: \ + component->component_init = PREFIX##_init; \ + component->component_update = PREFIX##_update; \ + component->component_draw = DRAW(PREFIX); \ + component->component_data = malloc(sizeof(PREFIX##_data_t)); \ + if(!component->component_data) \ + { \ + free(component); \ + error_printf("Failed to allocate memory : %d", errno); \ + return NULL; \ + } \ + component->component_deleter = PREFIX##_destroy; \ + break; +#include "components.def" + + default: + free(component); + error_printf("Component type doesn't exists : %d", component_type); + return NULL; + } + + return component; +} + +inline void destroy_component(component_t *component) +{ + component->component_deleter(component); + free(component); +} + +entity_t *create_entity(const unsigned int id) +{ + entity_t *entity = malloc(sizeof(entity_t)); + if(!entity) + { + error_printf("Failed to allocate memory : %d", errno); + return NULL; + } + + entity->id = id; + entity->draw_priority = 0; + + linked_list_init(&entity->components, sizeof(component_t), (deleter_t)destroy_component); + + return entity; +} + +void add_component(entity_t *entity, component_t *component, ...) +{ + component->entity = entity; + + va_list args, args_copy; + + va_start(args, component); + va_copy(args_copy, args); + + component->component_init(component, args_copy); + + va_end(args_copy); + va_end(args); + + linked_list_push_back(&entity->components, component); +} + +// Only for this file +// Arg : component_type_t component_type +static inline bool condition_is_component_type(elem_t *elem, va_list args) +{ + return ((component_t *)elem->data)->component_type == va_arg(args, component_type_t); +} + +inline component_t *get_component(entity_t *entity, component_type_t component_type) +{ + component_t *component = linked_list_get_if(&entity->components, condition_is_component_type, component_type); + if(!component) + { + error_printf("Entity doesn't have componant of type : %d", component_type); + return NULL; + } + + return component; +} + +// Only for this file +// No args +static inline int action_update_component(elem_t *elem, va_list args __attribute__((unused))) +{ + component_t *component = elem->data; + component->component_update(component); + + return EXIT_SUCCESS; +} + +inline void update_entity(entity_t *entity) +{ + linked_list_for_each(&entity->components, action_update_component); +} + +// Only for this file +// No args +static inline int action_draw_component(elem_t *elem, va_list args __attribute__((unused))) +{ + component_t *component = elem->data; + if(component->component_draw) component->component_draw(component); + return EXIT_SUCCESS; +} + +inline int draw_entity(entity_t *entity) +{ + return linked_list_for_each(&entity->components, action_draw_component); +} + +inline void destroy_entity(entity_t *entity) +{ + linked_list_clear(&entity->components); + free(entity); +} + +inline void entity_manager_init(entity_manager_t *entity_manager) +{ + linked_list_init(&entity_manager->entities, sizeof(entity_t), (deleter_t)destroy_entity); +} + +inline void entity_manager_add_entity(entity_manager_t *entity_manager, entity_t *entity) +{ + linked_list_push_back(&entity_manager->entities, entity); +} + +// Only for this file +// No args +static inline int action_update_entity(elem_t *elem, va_list args __attribute__((unused))) +{ + update_entity((entity_t *)elem->data); + return EXIT_SUCCESS; +} + +inline void entity_manager_update(entity_manager_t *entity_manager) +{ + linked_list_for_each(&entity_manager->entities, action_update_entity); +} + +// Only for this file +// No args +static inline int action_draw_entity(elem_t *elem, va_list args __attribute__((unused))) +{ + return draw_entity((entity_t *)elem->data); +} + +inline int entity_manager_draw(entity_manager_t *entity_manager) +{ + return linked_list_for_each(&entity_manager->entities, action_draw_entity); +} + +// Only for this file +// Arg : unsigned int id +static inline bool condition_is_entity_name(elem_t *elem, va_list args) +{ + return ((entity_t *)elem->data)->id == va_arg(args, unsigned int); +} + +inline void entity_manager_remove_entity(entity_manager_t *entity_manager, unsigned int id) +{ + linked_list_remove_if(&entity_manager->entities, condition_is_entity_name, id); +} + +inline void entity_manager_exit(entity_manager_t *entity_manager) +{ + linked_list_clear(&entity_manager->entities); +} diff --git a/src/event.c b/src/events.c similarity index 92% rename from src/event.c rename to src/events.c index 37cc008..497d33a 100644 --- a/src/event.c +++ b/src/events.c @@ -1,4 +1,4 @@ -#include "event.h" +#include "events.h" #include diff --git a/src/headers/asset_manager.h b/src/headers/asset_manager.h index 5dd14cc..4802a13 100644 --- a/src/headers/asset_manager.h +++ b/src/headers/asset_manager.h @@ -42,14 +42,14 @@ typedef struct asset_manager_t { FILE *asset_file; } asset_manager_t; -void asset_manager_init(asset_manager_t *asset_manager); -void asset_manager_exit(asset_manager_t *asset_manager); +int asset_manager_init(asset_manager_t *asset_manager); +int asset_manager_exit(asset_manager_t *asset_manager); void asset_manager_collect_garbage(asset_manager_t *asset_manager); -void asset_manager_load_asset(asset_manager_t *asset_manager, const char *asset_name, asset_type_t asset_type); -void asset_manager_let_go_asset(asset_manager_t *asset_manager, const char *asset_name); +int asset_manager_load_asset(asset_manager_t *asset_manager, const char *asset_name, asset_type_t asset_type); +int asset_manager_let_go_asset(asset_manager_t *asset_manager, const char *asset_name); texture_t *asset_manager_get_texture(asset_manager_t *asset_manager, const char *asset_name); -void asset_manager_draw_texture(texture_t *texture, rect_t *src_rect, rect_t *dst_rect, bool flip); +int asset_manager_draw_texture(texture_t *texture, rect_t *src_rect, rect_t *dst_rect, bool flip); #endif // ASSET_MANAGER_H \ No newline at end of file diff --git a/src/headers/display.h b/src/headers/display.h index a23c3e0..e30e561 100644 --- a/src/headers/display.h +++ b/src/headers/display.h @@ -2,7 +2,6 @@ #define DISPLAY_H #include -#include "vector2d.h" #define C_BLACK 0, 0, 0, 255 #define C_WHITE 255, 255, 255, 255 @@ -14,12 +13,11 @@ extern SDL_Window *window; extern SDL_Renderer *renderer; -void display_init(void); - -void display_update(void); - -void display_clear(void); - +int display_init(void); void display_exit(void); +int display_clear(void); +void display_update(void); + + #endif // DISPLAY_H \ No newline at end of file diff --git a/src/headers/ecs.h b/src/headers/ecs.h new file mode 100644 index 0000000..bacdf90 --- /dev/null +++ b/src/headers/ecs.h @@ -0,0 +1,78 @@ +#ifndef ECS_H +#define ECS_H + +#include +#include +#include "linked_list.h" + +typedef enum component_type_t { +#define COMPONENT(NAME, PREFIX, DRAW) NAME, +#include "components.def" +} component_type_t; + +// Component forwarding +typedef struct component_t component_t; + +// Component init function. It is called when component added to entity. This function is necessary. +typedef void (*component_init_t)(component_t *component, va_list args); +// Component update function. It is called in the main loop to update the component. This function is necessary. +typedef void (*component_update_t)(component_t *component); +// Component draw function. It is called in the main loop to draw the component on screen. This function is not necessary. +typedef int (*component_draw_t)(component_t *component); +// Component deleter function. It is called when the component is removed. It should free the data in the component data but not the component itself. +typedef void (*component_deleter_t)(component_t *component); + +typedef struct component_t { + component_init_t component_init; + component_update_t component_update; + component_draw_t component_draw; + + void *component_data; + + component_deleter_t component_deleter; + + struct entity_t *entity; + + component_type_t component_type; + +} component_t; + +component_t *create_component(component_type_t component_type); + +void destroy_component(component_t *component); + +typedef struct entity_t { + unsigned int id; + size_t draw_priority; + linked_list_t components; +} entity_t; + +entity_t *create_entity(const unsigned int id); + +void add_component(entity_t *entity, component_t *component, ...); + +component_t *get_component(entity_t *entity, component_type_t component_type); + +void update_entity(entity_t *entity); + +int draw_entity(entity_t *entity); + +void destroy_entity(entity_t *entity); + +typedef struct entity_manager_t { + linked_list_t entities; +} entity_manager_t; + +void entity_manager_init(entity_manager_t *entity_manager); + +void entity_manager_add_entity(entity_manager_t *entity_manager, entity_t *entity); + +void entity_manager_update(entity_manager_t *entity_manager); + +int entity_manager_draw(entity_manager_t *entity_manager); + +void entity_manager_remove_entity(entity_manager_t *entity_manager, const unsigned int id); + +void entity_manager_exit(entity_manager_t *entity_manager); + +#endif // ECS_H \ No newline at end of file diff --git a/src/headers/errors.h b/src/headers/errors.h new file mode 100644 index 0000000..59eda2c --- /dev/null +++ b/src/headers/errors.h @@ -0,0 +1,25 @@ +#ifndef ERRORS_H +#define ERRORS_H + +#include +#include + +#ifndef DEBUG +#define DEBUG 0 +#endif + +#if DEBUG > 0 +#define debug_printf(fmt, ...) \ + do { \ + fprintf(stderr, "\033[33mINFO\033[m: %s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + } while (0) +#else +#define debug_printf(fmt, ...) do {} while (0) +#endif + +#define error_printf(fmt, ...) \ + do { \ + fprintf(stderr, "\033[31mERROR\033[m: %s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + } while (0) + +#endif // ERRORS_H \ No newline at end of file diff --git a/src/headers/event.h b/src/headers/events.h similarity index 67% rename from src/headers/event.h rename to src/headers/events.h index 49e3601..2f2c276 100644 --- a/src/headers/event.h +++ b/src/headers/events.h @@ -2,6 +2,8 @@ #define KEYBOARD_H #include +#include "vector2d.h" +#include "keycodes.h" #define EVENT_KEY_UP SDL_KEYUP #define EVENT_KEY_DOWN SDL_KEYDOWN @@ -14,4 +16,9 @@ typedef struct event_t { int pollevent(event_t *event); +typedef struct events_t { + vector2d_t cursor_pos; + bool keys[MAX_KEYCODES]; +} events_t; + #endif // KEYBOARD_H \ No newline at end of file diff --git a/src/headers/game.h b/src/headers/game.h new file mode 100644 index 0000000..906d32d --- /dev/null +++ b/src/headers/game.h @@ -0,0 +1,46 @@ +#ifndef GAME_H +#define GAME_H + +#include +#include "asset_manager.h" +#include "ecs.h" +#include "events.h" + +/** + * @brief Game data. + * There should be only one instance of this struct. + * + * @param event Events data + * @param texture_manager Texture manager instance + * @param entity_manager Entity manager instance + * @param is_running Game running state + * @param delta_time_ms Frame time +*/ +typedef struct game_t { + asset_manager_t asset_manager; + entity_manager_t entity_manager; + events_t events; + + float delta_time_ms; + bool is_running; +} game_t; + +// Global game instance +extern game_t game; + +// Initialise game +int game_init(void); + +// Handle event like inputs +void game_handle_event(void); + +// Update game +void game_update(void); + +// Render game +int game_render(void); + +// Free game instance +int game_exit(void); + +#endif // GAME_H \ No newline at end of file diff --git a/src/headers/linked_list.h b/src/headers/linked_list.h index c3a3315..a6f01e1 100644 --- a/src/headers/linked_list.h +++ b/src/headers/linked_list.h @@ -56,14 +56,15 @@ typedef bool (*condition_t)(elem_t *elem, va_list args); * For function like linked_list_for_each you need an action to do. * It have two argument which are "elem_t *" and "va_list", that is because it passes the element on the actual index * and the args passed to the function. + * You can use the return value to indicate an error with EXIT_SUCCESS or EXIT_FAILURE. * Your condition_t function declaration must look like this : * - * void name_of_your_condition(elem_t *elem, va_list); + * int name_of_your_condition(elem_t *elem, va_list); * - * Then do whatever you want with the element data, just don't remove it. + * Then do whatever you want with the element data, just don't free it. * You should always define your function with "static", and "inline". */ -typedef void (*action_t)(elem_t *elem, va_list args); +typedef int (*action_t)(elem_t *elem, va_list args); /** * @brief Base of linked lists. @@ -282,7 +283,9 @@ void linked_list_remove_if(linked_list_t *linked_list, const condition_t conditi * @param linked_list Pointer to an linked list * @param action Action to apply * @param ... Argument to pass to the action + * + * @return The success of the action if supported by the action. */ -void linked_list_for_each(const linked_list_t *linked_list, const action_t action, ...); +int linked_list_for_each(const linked_list_t *linked_list, const action_t action, ...); #endif // LINKED_LIST_H diff --git a/src/linked_list.c b/src/linked_list.c index 6d7cf42..dcc9b6e 100644 --- a/src/linked_list.c +++ b/src/linked_list.c @@ -1,21 +1,27 @@ #include #include "memory_alloc.h" #include "linked_list.h" +#include "errors.h" elem_t *elem_create(const linked_list_t *linked_list) { - elem_t *tmp; - tmp = malloc(sizeof(elem_t)); - if(!tmp) return NULL; - - tmp->data = malloc(linked_list->data_size); - if(!tmp->data) + elem_t *elem; + elem = malloc(sizeof(elem_t)); + if(!elem) { - free(tmp); + error_printf("Failed to allocate memory : %d", errno); return NULL; } - return tmp; + elem->data = malloc(linked_list->data_size); + if(!elem->data) + { + error_printf("Failed to allocate memory : %d", errno); + free(elem); + return NULL; + } + + return elem; } inline void destroy_elem(elem_t *elem, const deleter_t elem_deleter) @@ -77,7 +83,11 @@ void linked_list_push_front_elem(linked_list_t *linked_list, elem_t *elem) void linked_list_push_back(linked_list_t *linked_list, void *data) { elem_t *tmp = malloc(sizeof(elem_t)); - if(!tmp) return; + if(!tmp) + { + error_printf("Failed to allocate memory : %d", errno); + return; + } tmp->data = data; linked_list_push_back_elem(linked_list, tmp); @@ -86,7 +96,11 @@ void linked_list_push_back(linked_list_t *linked_list, void *data) void linked_list_push_front(linked_list_t *linked_list, void *data) { elem_t *tmp = malloc(sizeof(elem_t)); - if(!tmp) return; + if(!tmp) + { + error_printf("Failed to allocate memory : %d", errno); + return; + } tmp->data = data; linked_list_push_front_elem(linked_list, tmp); @@ -179,7 +193,11 @@ elem_t *linked_list_get_elem(const linked_list_t *linked_list, size_t index) void *linked_list_get(const linked_list_t *linked_list, size_t index) { elem_t *tmp = linked_list_get_elem(linked_list, index); - if(!tmp) return NULL; + if(!tmp) + { + error_printf("Failed to get elem."); + return NULL; + } return tmp->data; } @@ -214,7 +232,11 @@ void linked_list_insert_elem(linked_list_t *linked_list, elem_t *elem, size_t in void linked_list_insert(linked_list_t *linked_list, void *data, size_t index) { elem_t *tmp = malloc(sizeof(elem_t)); - if(!tmp) return; + if(!tmp) + { + error_printf("Failed to allocate memory : %d", errno); + return; + } tmp->data = data; linked_list_insert_elem(linked_list, tmp, index); @@ -324,7 +346,11 @@ void *linked_list_get_if(const linked_list_t *linked_list, const condition_t con void linked_list_remove_if(linked_list_t *linked_list, const condition_t condition, ...) { - if(!condition) return; + if(!condition) + { + error_printf("Condition cannot be NULL."); + return; + } va_list args; va_start(args, condition); @@ -352,9 +378,13 @@ void linked_list_remove_if(linked_list_t *linked_list, const condition_t conditi va_end(args); } -void linked_list_for_each(const linked_list_t *linked_list, const action_t action, ...) +int linked_list_for_each(const linked_list_t *linked_list, const action_t action, ...) { - if(!action) return; + if(!action) + { + error_printf("Action cannot be NULL."); + return EXIT_FAILURE; + } va_list args; va_start(args, action); @@ -366,7 +396,13 @@ void linked_list_for_each(const linked_list_t *linked_list, const action_t actio va_list args_copy; va_copy(args_copy, args); - action(actual_elem, args_copy); + if(action(actual_elem, args_copy)) + { + va_end(args_copy); + va_end(args); + + return EXIT_FAILURE; + } va_end(args_copy); @@ -374,4 +410,6 @@ void linked_list_for_each(const linked_list_t *linked_list, const action_t actio } va_end(args); + + return EXIT_SUCCESS; } diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..a894135 --- /dev/null +++ b/src/main.c @@ -0,0 +1,21 @@ +#include "game.h" + +game_t game; + +int main(void) +{ + int exit_status = EXIT_SUCCESS; + + if(game_init()) exit_status = EXIT_FAILURE; + + while(game.is_running) + { + game_handle_event(); + game_update(); + if(game_render()) exit_status = EXIT_FAILURE; + } + + if(game_exit()) exit_status = EXIT_FAILURE; + + return exit_status; +}