Pico Headers
Loading...
Searching...
No Matches
pico_ecs.h
Go to the documentation of this file.
1
162#ifndef PICO_ECS_H
163#define PICO_ECS_H
164
165#include <stdbool.h> // bool, true, false
166#include <stddef.h> // size_t
167#include <stdint.h> // uint32_t, uint64_t
168#include <limits.h> // SIZE_MAX
169
170#ifdef __cplusplus
171extern "C" {
172#endif
173
177typedef struct ecs_s ecs_t;
178
182#ifndef ECS_ID_TYPE
183#define ECS_ID_TYPE uint64_t
184#endif
185
190
194#ifndef ECS_MASK_TYPE
195#define ECS_MASK_TYPE uint64_t
196#endif
197
202
206typedef int32_t ecs_ret_t;
207
212
216typedef struct ecs_comp_t { ecs_id_t id; } ecs_comp_t;
217
222
226#define ECS_INVALID_ID ((ecs_id_t)-1)
227
231#define ECS_IS_INVALID(obj) ((obj.id) == ECS_INVALID_ID)
232
241ecs_t* ecs_new(size_t entity_capacity, void* mem_ctx);
242
248void ecs_free(ecs_t* ecs);
249
253void ecs_reset(ecs_t* ecs);
254
262typedef void (*ecs_on_add_fn)(ecs_t* ecs,
263 ecs_entity_t entity,
264 ecs_comp_t comp,
265 const void* args,
266 void* udata);
267
275typedef void (*ecs_on_remove_fn)(ecs_t* ecs,
276 ecs_entity_t entity,
277 ecs_comp_t comp,
278 void* udata);
279
280
289typedef void (*ecs_on_set_fn)(ecs_t* ecs,
290 ecs_entity_t entity,
291 ecs_comp_t comp,
292 void* udata);
293
317
330 size_t size,
331 const ecs_comp_desc_t* desc);
332
345 ecs_entity_t* entities,
346 size_t entity_count,
347 void* udata);
348
356typedef void (*ecs_on_join_fn)(ecs_t* ecs, ecs_entity_t entity, void* udata);
357
365typedef void (*ecs_on_leave_fn)(ecs_t* ecs, ecs_entity_t entity, void* udata);
366
383
396 ecs_system_fn system_cb,
397 const ecs_sys_desc_t* desc);
398
399
409
419
427
435
446 ecs_system_t sys,
447 ecs_system_fn system_cb,
448 ecs_on_join_fn on_join,
449 ecs_on_leave_fn on_leave);
450
458void ecs_set_system_udata(ecs_t* ecs, ecs_system_t sys, void* udata);
459
468
477
486
491
496
505
513bool ecs_is_ready(ecs_t* ecs, ecs_entity_t entity);
514
524bool ecs_has(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp);
525
541void ecs_add(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp, void* args);
542
552void* ecs_get(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp);
553
566void ecs_set(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp, void* data);
567
576void ecs_destroy(ecs_t* ecs, ecs_entity_t entity);
577
585void ecs_remove(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp);
586
597
609
610#ifdef __cplusplus
611}
612#endif
613
614#endif // PICO_ECS_H
615
616#ifdef PICO_ECS_IMPLEMENTATION // Define once
617
618#ifndef PICO_ECS_MAX_COMPONENTS
619#define PICO_ECS_MAX_COMPONENTS 32
620#endif
621
622#ifndef PICO_ECS_MAX_SYSTEMS
623#define PICO_ECS_MAX_SYSTEMS 16
624#endif
625
626#ifndef PICO_ECS_COMP_BLOCK_SIZE
627#define PICO_ECS_COMP_BLOCK_SIZE 64
628#endif
629
630#ifdef NDEBUG
631 #define PICO_ECS_ASSERT(expr) ((void)0)
632#else
633 #ifndef PICO_ECS_ASSERT
634 #include <assert.h>
635 #define PICO_ECS_ASSERT(expr) (assert(expr))
636 #endif
637#endif
638
639#if !defined(PICO_ECS_MALLOC) || !defined(PICO_ECS_REALLOC) || !defined(PICO_ECS_FREE)
640#include <stdlib.h>
641#define PICO_ECS_MALLOC(size, ctx) (malloc(size))
642#define PICO_ECS_REALLOC(ptr, size, ctx) (realloc(ptr, size))
643#define PICO_ECS_FREE(ptr, ctx) (free(ptr))
644#endif
645
646#ifndef PICO_ECS_MEMSET
647 #include <string.h>
648 #define PICO_ECS_MEMSET memset
649#endif
650
651#ifndef PICO_ECS_MEMCPY
652 #include <string.h>
653 #define PICO_ECS_MEMCPY memcpy
654#endif
655
656#include <stdalign.h>
657
658/*=============================================================================
659 * Aliases>
660 *============================================================================*/
661
662#define ECS_ASSERT PICO_ECS_ASSERT
663#define ECS_MAX_COMPONENTS PICO_ECS_MAX_COMPONENTS
664#define ECS_MAX_SYSTEMS PICO_ECS_MAX_SYSTEMS
665#define ECS_COMP_BLOCK_SIZE PICO_ECS_COMP_BLOCK_SIZE
666#define ECS_MALLOC PICO_ECS_MALLOC
667#define ECS_REALLOC PICO_ECS_REALLOC
668#define ECS_FREE PICO_ECS_FREE
669#define ECS_MEMSET PICO_ECS_MEMSET
670#define ECS_MEMCPY PICO_ECS_MEMCPY
671
672/*=============================================================================
673 * Data structures
674 *============================================================================*/
675
676#if ECS_MAX_COMPONENTS <= 32
677typedef uint32_t ecs_bitset_t;
678#elif ECS_MAX_COMPONENTS <= 64
679typedef uint64_t ecs_bitset_t;
680#else
681#define ECS_BITSET_WIDTH 64
682#define ECS_BITSET_SIZE (((ECS_MAX_COMPONENTS - 1) / ECS_BITSET_WIDTH) + 1)
683
684typedef struct
685{
686 uint64_t array[ECS_BITSET_SIZE];
687} ecs_bitset_t;
688
689#endif // ECS_MAX_COMPONENTS
690
691typedef struct ecs_arena_block_s
692{
693 uint8_t* memory;
694 size_t size;
695 size_t offset;
696 struct ecs_arena_block_s* next;
697} ecs_arena_block_t;
698
699typedef struct ecs_arena_s
700{
701 ecs_t* ecs;
702 ecs_arena_block_t* first;
703 ecs_arena_block_t* current;
704 size_t block_size;
705} ecs_arena_t;
706
707// Data-structure for a packed array implementation that provides O(1) functions
708// for adding, removing, and accessing entity IDs
709typedef struct
710{
711 size_t capacity;
712 size_t size;
713 size_t* sparse;
714 ecs_entity_t* dense;
715} ecs_sparse_set_t;
716
717// A data-structure for providing O(1) operations for working with IDs
718typedef struct
719{
720 size_t capacity;
721 size_t size; // array size
722 ecs_id_t* data;
723} ecs_id_array_t;
724
725typedef struct
726{
727 size_t block_count; // number of allocated blocks
728 size_t block_capacity; // capacity of the blocks[] pointer array
729 size_t comp_size; // size of one component in bytes
730 void** blocks; // array of pointers to fixed-size blocks
731} ecs_comp_blocks_t;
732
733typedef struct
734{
735 ecs_bitset_t comp_bits;
736 bool active;
737 bool ready;
738} ecs_entity_data_t;
739
740typedef struct
741{
742 ecs_on_add_fn on_add;
743 ecs_on_remove_fn on_remove;
744 ecs_on_set_fn on_set;
745 size_t size;
746 void* default_value;
747 size_t args_size;
748 void* udata;
749} ecs_comp_data_t;
750
751typedef struct
752{
753 bool active;
754 ecs_sparse_set_t entity_ids;
755 ecs_mask_t mask;
756 ecs_system_fn system_cb;
757 ecs_on_join_fn on_join;
758 ecs_on_leave_fn on_leave;
759 ecs_bitset_t require_bits;
760 ecs_bitset_t exclude_bits;
761 void* udata;
762} ecs_sys_data_t;
763
764typedef enum
765{
766 ECS_CMD_ADD,
767 ECS_CMD_REMOVE,
768 ECS_CMD_SET,
769 ECS_CMD_DESTROY,
770} ecs_cmd_type_t;
771
772typedef struct
773{
774 ecs_cmd_type_t type;
775 ecs_entity_t entity;
776 ecs_comp_t comp;
777 void* data;
778 void* args;
779} ecs_cmd_t;
780
781typedef struct
782{
783 ecs_cmd_t* data;
784 size_t size;
785 size_t capacity;
786} ecs_cmd_array_t;
787
788struct ecs_s
789{
790 ecs_id_array_t entity_pool;
791 ecs_entity_data_t* entities;
792 size_t entity_capacity;
793 size_t next_entity_id;
794 ecs_comp_data_t comps[ECS_MAX_COMPONENTS];
795 ecs_comp_blocks_t comp_blocks[ECS_MAX_COMPONENTS];
796 size_t comp_count;
797 ecs_sys_data_t systems[ECS_MAX_SYSTEMS];
798 size_t system_count;
799 bool system_active;
800 ecs_cmd_array_t cmd_queue;
801 ecs_arena_t arena;
802 void* mem_ctx;
803};
804
805/*=============================================================================
806 * Handle constructors
807 *============================================================================*/
808static inline ecs_entity_t ecs_make_entity(ecs_id_t id);
809static inline ecs_comp_t ecs_make_comp(ecs_id_t id);
810static inline ecs_system_t ecs_make_system(ecs_id_t id);
811
812/*=============================================================================
813 * Realloc wrapper
814 *============================================================================*/
815static void* ecs_realloc_zero(ecs_t* ecs, void* ptr, size_t old_size, size_t new_size);
816
817/*=============================================================================
818 * Tests if entity is active (created)
819 *============================================================================*/
820static inline bool ecs_is_active(ecs_t* ecs, ecs_id_t entity_id);
821
822/*=============================================================================
823 * Command queue functions
824 *============================================================================*/
825static void ecs_cmd_array_init(ecs_t* ecs, ecs_cmd_array_t* queue, size_t capacity);
826static void ecs_cmd_array_free(ecs_t* ecs, ecs_cmd_array_t* queue);
827static ecs_cmd_t* ecs_cmd_array_push(ecs_t* ecs, ecs_cmd_array_t* queue);
828static void ecs_cmd_flush_queue(ecs_t* ecs);
829
830/*=============================================================================
831 * Bitset functions
832 *============================================================================*/
833static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on);
834static inline bool ecs_bitset_is_zero(ecs_bitset_t* set);
835static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit);
836static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1, ecs_bitset_t* set2);
837static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1, ecs_bitset_t* set2);
838static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set);
839static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2);
840static inline bool ecs_bitset_true(ecs_bitset_t* set);
841
842/*=============================================================================
843 * Arena functions
844 *============================================================================*/
845static ecs_arena_block_t* ecs_arena_block_create(ecs_t* ecs, size_t size);
846static bool ecs_arena_init(ecs_t* ecs, ecs_arena_t* arena, size_t initial_block_size);
847static bool ecs_arena_grow(ecs_t* ecs, ecs_arena_t* arena, size_t min_size);
848static uintptr_t ecs_arena_align_forward(uintptr_t ptr, size_t align);
849static void* ecs_arena_alloc_align(ecs_t* ecs, ecs_arena_t* arena, size_t size, size_t align);
850static void* ecs_arena_alloc(ecs_t* ecs, ecs_arena_t* arena, size_t size);
851static void ecs_arena_reset(ecs_t* ecs, ecs_arena_t* arena);
852static void ecs_arena_destroy(ecs_t* ecs, ecs_arena_t* arena);
853
854/*=============================================================================
855 * Sparse set functions
856 *============================================================================*/
857static void ecs_sparse_set_init(ecs_t* ecs, ecs_sparse_set_t* set, size_t capacity);
858static void ecs_sparse_set_free(ecs_t* ecs, ecs_sparse_set_t* set);
859static bool ecs_sparse_set_add(ecs_t* ecs, ecs_sparse_set_t* set, ecs_id_t id);
860static inline bool ecs_sparse_set_find(ecs_sparse_set_t* set, ecs_id_t id, size_t* found);
861static inline bool ecs_sparse_set_remove(ecs_sparse_set_t* set, ecs_id_t id);
862
863/*=============================================================================
864 * System entity add/remove functions
865 *============================================================================*/
866
867static bool ecs_entity_system_test(ecs_bitset_t require_bits,
868 ecs_bitset_t exclude_bits,
869 ecs_bitset_t entity_bits);
870
871static void ecs_sync_add_remove(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id);
872static void ecs_sync_destroy(ecs_t* ecs, ecs_id_t entity_id);
873
874/*=============================================================================
875 * ID array functions
876 *============================================================================*/
877static void ecs_id_array_init(ecs_t* ecs, ecs_id_array_t* pool, size_t capacity);
878static void ecs_id_array_free(ecs_t* ecs, ecs_id_array_t* pool);
879static inline void ecs_id_array_push(ecs_t* ecs, ecs_id_array_t* pool, ecs_id_t id);
880static inline ecs_id_t ecs_id_array_pop(ecs_id_array_t* pool);
881static inline size_t ecs_id_array_size(ecs_id_array_t* pool);
882
883/*=============================================================================
884 * Component array functions
885 *============================================================================*/
886static void ecs_comp_blocks_init(ecs_t* ecs, ecs_comp_blocks_t* array, size_t size, size_t capacity);
887static void ecs_comp_blocks_free(ecs_t* ecs, ecs_comp_blocks_t* array);
888static void ecs_comp_blocks_resize(ecs_t* ecs, ecs_comp_blocks_t* array, ecs_id_t id);
889
890/*=============================================================================
891 * Validation functions
892 *============================================================================*/
893#ifndef NDEBUG
894static bool ecs_is_not_null(void* ptr);
895static bool ecs_is_valid_component_id(ecs_id_t id);
896static bool ecs_is_valid_system_id(ecs_id_t id);
897static bool ecs_is_valid_id(ecs_id_t id);
898static bool ecs_is_valid_capacity(size_t capacity, size_t elem_size);
899static bool ecs_is_entity_ready(ecs_t* ecs, ecs_id_t entity_id);
900static bool ecs_is_component_ready(ecs_t* ecs, ecs_id_t comp_id);
901static bool ecs_is_system_ready(ecs_t* ecs, ecs_id_t sys_id);
902#endif // NDEBUG
903
904/*=============================================================================
905 * Public API implementation
906 *============================================================================*/
907
908ecs_t* ecs_new(size_t entity_capacity, void* mem_ctx)
909{
910 ECS_ASSERT(entity_capacity > 0);
911 ECS_ASSERT(!ecs_is_valid_id(ECS_INVALID_ID) && "ecs_id_t is signed");
912
913 ecs_t* ecs = (ecs_t*)ECS_MALLOC(sizeof(ecs_t), mem_ctx);
914
915 // Out of memory
916 if (NULL == ecs)
917 return NULL;
918
919 ECS_MEMSET(ecs, 0, sizeof(ecs_t));
920
921 ecs->entity_capacity = (entity_capacity > 0) ? entity_capacity : 32;
922 ecs->next_entity_id = 0;
923 ecs->system_active = false;
924 ecs->mem_ctx = mem_ctx;
925
926 // Initialize entity pool and queues
927 ecs_id_array_init(ecs, &ecs->entity_pool, entity_capacity);
928
929 // Initialize deferred command queue
930 ecs_cmd_array_init(ecs, &ecs->cmd_queue, entity_capacity);
931
932 // Allocate entity array
933 ECS_ASSERT(ecs_is_valid_capacity(ecs->entity_capacity, sizeof(ecs_entity_data_t)));
934 ecs->entities = (ecs_entity_data_t*)ECS_MALLOC(ecs->entity_capacity * sizeof(ecs_entity_data_t),
935 ecs->mem_ctx);
936
937 // Zero entity array
938 ECS_MEMSET(ecs->entities, 0, ecs->entity_capacity * sizeof(ecs_entity_data_t));
939
940 ecs_arena_init(ecs, &ecs->arena, 512);
941
942 return ecs;
943}
944
945void ecs_free(ecs_t* ecs)
946{
947 ECS_ASSERT(ecs_is_not_null(ecs));
948
949 ecs_id_array_free(ecs, &ecs->entity_pool);
950 ecs_cmd_array_free(ecs, &ecs->cmd_queue);
951 ecs_arena_destroy(ecs, &ecs->arena);
952
953 for (ecs_id_t comp_id = 0; comp_id < ecs->comp_count; comp_id++)
954 {
955 ecs_comp_blocks_t* comp_blocks = &ecs->comp_blocks[comp_id];
956 ecs_comp_blocks_free(ecs, comp_blocks);
957 }
958
959 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
960 {
961 ecs_sys_data_t* sys = &ecs->systems[sys_id];
962 ecs_sparse_set_free(ecs, &sys->entity_ids);
963 }
964
965 for (ecs_id_t comp_id = 0; comp_id < ecs->comp_count; comp_id++)
966 {
967 ecs_comp_data_t* comp_data = &ecs->comps[comp_id];
968
969 if (comp_data->default_value)
970 {
971 ECS_FREE(comp_data->default_value, ecs->mem_ctx);
972 }
973 }
974
975 ECS_FREE(ecs->entities, ecs->mem_ctx);
976 ECS_FREE(ecs, ecs->mem_ctx);
977}
978
979void ecs_reset(ecs_t* ecs)
980{
981 ECS_ASSERT(ecs_is_not_null(ecs));
982
983 ecs->entity_pool.size = 0;
984
985 ECS_MEMSET(ecs->entities, 0, ecs->entity_capacity * sizeof(ecs_entity_data_t));
986
987 ecs->next_entity_id = 0;
988
989 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
990 {
991 ecs->systems[sys_id].entity_ids.size = 0;
992 }
993}
994
996 size_t size,
997 const ecs_comp_desc_t* desc)
998{
999 ECS_ASSERT(ecs_is_not_null(ecs));
1000 ECS_ASSERT(ecs->comp_count < ECS_MAX_COMPONENTS);
1001 ECS_ASSERT(size > 0);
1002
1003 ecs_comp_t comp = ecs_make_comp(ecs->comp_count);
1004
1005 ecs_comp_blocks_t* comp_blocks = &ecs->comp_blocks[comp.id];
1006 ecs_comp_blocks_init(ecs, comp_blocks, size, ecs->entity_capacity);
1007
1008 ecs_comp_data_t* comp_data = &ecs->comps[comp.id];
1009
1010 ECS_MEMSET(comp_data, 0, sizeof(ecs_comp_data_t));
1011 comp_data->size = size;
1012
1013 if (desc)
1014 {
1015 comp_data->on_add = desc->on_add_cb;
1016 comp_data->on_remove = desc->on_remove_cb;
1017 comp_data->on_set = desc->on_set_cb;
1018 comp_data->args_size = desc->args_size;
1019 comp_data->udata = desc->udata;
1020
1021 if (desc->default_value)
1022 {
1023 comp_data->default_value = ECS_MALLOC(size, ctx->mem_ctx);
1024 ECS_MEMCPY(comp_data->default_value, desc->default_value, size);
1025 }
1026 }
1027
1028 ecs->comp_count++;
1029
1030 return comp;
1031}
1032
1034 ecs_system_fn system_cb,
1035 const ecs_sys_desc_t* desc)
1036{
1037 ECS_ASSERT(ecs_is_not_null(ecs));
1038 ECS_ASSERT(ecs->system_count < ECS_MAX_SYSTEMS);
1039 ECS_ASSERT(NULL != system_cb);
1040
1041 ecs_system_t sys = ecs_make_system(ecs->system_count);
1042 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1043
1044 ECS_MEMSET(sys_data, 0, sizeof(ecs_sys_data_t));
1045
1046 ecs_sparse_set_init(ecs, &sys_data->entity_ids, ecs->entity_capacity);
1047
1048 sys_data->system_cb = system_cb;
1049 sys_data->active = true;
1050
1051 if (desc)
1052 {
1053 sys_data->mask = desc->mask;
1054 sys_data->on_join = desc->on_join_cb;
1055 sys_data->on_leave = desc->on_leave_cb;
1056 sys_data->udata = desc->udata;
1057 }
1058
1059 ecs->system_count++;
1060
1061 return sys;
1062}
1063
1064void ecs_require(ecs_t* ecs, ecs_system_t sys, ecs_comp_t comp)
1065{
1066 ECS_ASSERT(ecs_is_not_null(ecs));
1067 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1068 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1069 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1070 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1071
1072 // Set system component bit for the specified component
1073 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1074 ecs_bitset_flip(&sys_data->require_bits, comp.id, true);
1075}
1076
1077void ecs_exclude(ecs_t* ecs, ecs_system_t sys, ecs_comp_t comp)
1078{
1079 ECS_ASSERT(ecs_is_not_null(ecs));
1080 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1081 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1082 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1083 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1084
1085 // Set system component bit for the specified component
1086 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1087 ecs_bitset_flip(&sys_data->exclude_bits, comp.id, true);
1088}
1089
1090void ecs_enable_system(ecs_t* ecs, ecs_system_t sys)
1091{
1092 ECS_ASSERT(ecs_is_not_null(ecs));
1093 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1094 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1095
1096 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1097 sys_data->active = true;
1098}
1099
1101{
1102 ECS_ASSERT(ecs_is_not_null(ecs));
1103 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1104 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1105
1106 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1107 sys_data->active = false;
1108}
1109
1111 ecs_system_t sys,
1112 ecs_system_fn system_cb,
1113 ecs_on_join_fn on_join,
1114 ecs_on_leave_fn on_leave)
1115{
1116 ECS_ASSERT(ecs_is_not_null(ecs));
1117 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1118 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1119 ECS_ASSERT(NULL != system_cb);
1120
1121 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1122 sys_data->system_cb = system_cb;
1123 sys_data->on_join = on_join;
1124 sys_data->on_leave = on_leave;
1125}
1126
1127void ecs_set_system_udata(ecs_t* ecs, ecs_system_t sys, void* udata)
1128{
1129 ECS_ASSERT(ecs_is_not_null(ecs));
1130 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1131 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1132
1133 ecs->systems[sys.id].udata = udata;
1134}
1135
1137{
1138 ECS_ASSERT(ecs_is_not_null(ecs));
1139 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1140 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1141
1142 return ecs->systems[sys.id].udata;
1143}
1144
1146{
1147 ECS_ASSERT(ecs_is_not_null(ecs));
1148 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1149 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1150
1151 ecs->systems[sys.id].mask = mask;
1152}
1153
1155{
1156 ECS_ASSERT(ecs_is_not_null(ecs));
1157 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1158 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1159
1160 return ecs->systems[sys.id].mask;
1161}
1162
1164{
1165 ECS_ASSERT(ecs_is_not_null(ecs));
1166 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1167 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1168
1169 return ecs->systems[sys.id].entity_ids.dense;
1170}
1171
1172size_t ecs_get_entity_count(ecs_t* ecs, ecs_system_t sys)
1173{
1174 ECS_ASSERT(ecs_is_not_null(ecs));
1175 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1176 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1177
1178 return ecs->systems[sys.id].entity_ids.size;
1179}
1180
1182{
1183 ECS_ASSERT(ecs_is_not_null(ecs));
1184
1185 ecs_id_t entity_id = 0;
1186
1187 // If there is an ID in the pool, pop it
1188 ecs_id_array_t* pool = &ecs->entity_pool;
1189
1190 if (0 != ecs_id_array_size(pool))
1191 {
1192 entity_id = ecs_id_array_pop(pool);
1193 }
1194 else
1195 {
1196 // Otherwise, issue a fresh ID
1197 entity_id = ecs->next_entity_id++;
1198
1199 // Grow the entities array if necessary
1200 if (entity_id >= ecs->entity_capacity)
1201 {
1202 size_t old_capacity = ecs->entity_capacity;
1203 size_t new_capacity = 2 * old_capacity;
1204
1205 ECS_ASSERT(ecs_is_valid_capacity(new_capacity, sizeof(ecs_entity_data_t)));
1206 ecs->entities = (ecs_entity_data_t*)ecs_realloc_zero(ecs, ecs->entities,
1207 old_capacity * sizeof(ecs_entity_data_t),
1208 new_capacity * sizeof(ecs_entity_data_t));
1209
1210 ecs->entity_capacity = new_capacity;
1211 }
1212 }
1213
1214 // Activate the entity and return a handle
1215 ecs->entities[entity_id].active = true;
1216 ecs->entities[entity_id].ready = true;
1217
1218 return ecs_make_entity(entity_id);
1219}
1220
1221bool ecs_is_ready(ecs_t* ecs, ecs_entity_t entity)
1222{
1223 ECS_ASSERT(ecs_is_not_null(ecs));
1224
1225 return ecs->entities[entity.id].ready;
1226}
1227
1228void ecs_set(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp, void* data)
1229{
1230 ECS_ASSERT(ecs_is_not_null(ecs));
1231 ECS_ASSERT(ecs_is_valid_id(entity.id));
1232 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1233 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1234 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1235
1236 if (!ecs_has(ecs, entity, comp))
1237 {
1238 ecs_add(ecs, entity, comp, NULL);
1239 }
1240
1241 ecs_comp_data_t* comp_data = &ecs->comps[comp.id];
1242
1243 if (ecs->system_active)
1244 {
1245 ecs_cmd_t* cmd = ecs_cmd_array_push(ecs, &ecs->cmd_queue);
1246 cmd->type = ECS_CMD_SET;
1247 cmd->entity = entity;
1248 cmd->comp = comp;
1249 cmd->data = ecs_arena_alloc(ecs, &ecs->arena, comp_data->size);
1250 ECS_MEMCPY(cmd->data, data, comp_data->size);
1251 return;
1252 }
1253
1254 void* comp_ptr = ecs_get(ecs, entity, comp);
1255 ECS_MEMCPY(comp_ptr, data, comp_data->size);
1256
1257 // Callback
1258 if (comp_data->on_set)
1259 comp_data->on_set(ecs, entity, comp, comp_data->udata);
1260}
1261
1262void ecs_destroy(ecs_t* ecs, ecs_entity_t entity)
1263{
1264 ECS_ASSERT(ecs_is_not_null(ecs));
1265 ECS_ASSERT(ecs_is_valid_id(entity.id));
1266 ECS_ASSERT(ecs_is_active(ecs, entity.id));
1267
1268 if (!ecs_is_active(ecs, entity.id))
1269 return;
1270
1271 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1272 ecs_bitset_t comp_bits = entity_data->comp_bits;
1273
1274 if (ecs->system_active)
1275 {
1276 ecs_cmd_t* cmd = ecs_cmd_array_push(ecs, &ecs->cmd_queue);
1277 cmd->type = ECS_CMD_DESTROY;
1278 cmd->entity = entity;
1279 ecs->entities[entity.id].ready = false;
1280 return;
1281 }
1282
1283 for (ecs_id_t comp_id = 0; comp_id < ecs->comp_count; comp_id++)
1284 {
1285 if (ecs_bitset_test(&comp_bits, comp_id))
1286 {
1287 ecs_comp_data_t* comp_data = &ecs->comps[comp_id];
1288
1289 if (comp_data->on_remove)
1290 {
1291 ecs_comp_t comp = ecs_make_comp(comp_id);
1292 comp_data->on_remove(ecs, entity, comp, comp_data->udata);
1293 }
1294 }
1295 }
1296
1297 ecs_sync_destroy(ecs, entity.id);
1298
1299 ecs_id_array_t* pool = &ecs->entity_pool;
1300 ecs_id_array_push(ecs, pool, entity.id);
1301
1302 ECS_MEMSET(&entity_data->comp_bits, 0, sizeof(ecs_bitset_t));
1303 entity_data->active = false;
1304 entity_data->ready = false;
1305}
1306
1307bool ecs_has(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp)
1308{
1309 ECS_ASSERT(ecs_is_not_null(ecs));
1310 ECS_ASSERT(ecs_is_valid_id(entity.id));
1311 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1312
1313 // Load entity data
1314 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1315
1316 if (!entity_data->ready)
1317 return false;
1318
1319 // Return true if the component belongs to the entity
1320 return ecs_bitset_test(&entity_data->comp_bits, comp.id);
1321}
1322
1323void* ecs_get(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp)
1324{
1325 ECS_ASSERT(ecs_is_not_null(ecs));
1326 ECS_ASSERT(ecs_is_valid_id(entity.id));
1327 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1328 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1329 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1330
1331 // Map entity ID to block and slot within that block.
1332 // Blocks are never reallocated, so returned pointers remain stable.
1333 ecs_comp_blocks_t* comp_blocks = &ecs->comp_blocks[comp.id];
1334
1335 size_t block = entity.id / ECS_COMP_BLOCK_SIZE;
1336 size_t slot = entity.id % ECS_COMP_BLOCK_SIZE;
1337
1338 return (char*)comp_blocks->blocks[block] + (comp_blocks->comp_size * slot);
1339}
1340
1341void ecs_add(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp, void* args)
1342{
1343 ECS_ASSERT(ecs_is_not_null(ecs));
1344 ECS_ASSERT(ecs_is_valid_id(entity.id));
1345 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1346 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1347 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1348
1349 if (ecs_has(ecs, entity, comp))
1350 return;
1351
1352 // Load entity data
1353 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1354
1355 // Set entity component bit that determines which systems this entity
1356 // belongs to
1357 ecs_bitset_flip(&entity_data->comp_bits, comp.id, true);
1358
1359 // Load component
1360 ecs_comp_blocks_t* comp_blocks = &ecs->comp_blocks[comp.id];
1361
1362 // Grow the component array now (not deferred) so that ecs_get can safely
1363 // index into it immediately, since ecs_has already reports the component
1364 // as present as soon as the bit above is flipped
1365 ecs_comp_blocks_resize(ecs, comp_blocks, entity.id);
1366
1367 ecs_comp_data_t* comp_data = &ecs->comps[comp.id];
1368
1369 if (ecs->system_active)
1370 {
1371 ecs_cmd_t* cmd = ecs_cmd_array_push(ecs, &ecs->cmd_queue);
1372 cmd->type = ECS_CMD_ADD;
1373 cmd->entity = entity;
1374 cmd->comp = comp;
1375
1376 // Copy the constructor args into the command arena so the caller is
1377 // not required to keep them alive until the queue is flushed. The copy
1378 // is handed back to ecs_add (and thus the on_add constructor) when the
1379 // queue is flushed at the end of the system run.
1380 if (args && comp_data->args_size > 0)
1381 {
1382 cmd->args = ecs_arena_alloc(ecs, &ecs->arena, comp_data->args_size);
1383 ECS_MEMCPY(cmd->args, args, comp_data->args_size);
1384 }
1385
1386 return;
1387 }
1388
1389 // Get pointer to component
1390 void* comp_ptr = ecs_get(ecs, entity, comp);
1391
1392 // Set default value
1393 if (comp_data->default_value)
1394 ECS_MEMCPY(comp_ptr, comp_data->default_value, comp_data->size);
1395 else
1396 ECS_MEMSET(comp_ptr, 0, comp_blocks->comp_size);
1397
1398 // Call constructor
1399 if (comp_data->on_add)
1400 comp_data->on_add(ecs, entity, comp, args, comp_data->udata);
1401
1402 // Add/remove entity to/from systems based on matching criteria
1403 ecs_sync_add_remove(ecs, entity.id, comp.id);
1404}
1405
1406void ecs_remove(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp)
1407{
1408 ECS_ASSERT(ecs_is_not_null(ecs));
1409 ECS_ASSERT(ecs_is_valid_id(entity.id));
1410 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1411 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1412 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1413
1414 if (!ecs_has(ecs, entity, comp))
1415 return;
1416
1417 // Load entity data
1418 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1419
1420 // Set entity component bit that determines which systems this entity
1421 // belongs to
1422 ecs_bitset_flip(&entity_data->comp_bits, comp.id, false);
1423
1424 if (ecs->system_active)
1425 {
1426 ecs_cmd_t* cmd = ecs_cmd_array_push(ecs, &ecs->cmd_queue);
1427 cmd->type = ECS_CMD_REMOVE;
1428 cmd->entity = entity;
1429 cmd->comp = comp;
1430 return;
1431 }
1432
1433 // Fire callback
1434 ecs_comp_data_t* comp_data = &ecs->comps[comp.id];
1435
1436 if (comp_data->on_remove)
1437 comp_data->on_remove(ecs, entity, comp, comp_data->udata);
1438
1439 // Add/remove entity to/from systems based on matching criteria
1440 ecs_sync_add_remove(ecs, entity.id, comp.id);
1441}
1442
1444{
1445 ECS_ASSERT(ecs_is_not_null(ecs));
1446 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1447 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1448
1449 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1450
1451 if (!sys_data->active)
1452 return 0;
1453
1454 if (0 != sys_data->mask && !(sys_data->mask & mask))
1455 return 0;
1456
1457 ecs->system_active = true;
1458
1459 ecs_ret_t code = sys_data->system_cb(ecs,
1460 sys_data->entity_ids.dense,
1461 sys_data->entity_ids.size,
1462 sys_data->udata);
1463
1464 ecs->system_active = false;
1465
1466 ecs_cmd_flush_queue(ecs);
1467 ecs_arena_reset(ecs, &ecs->arena);
1468
1469 return code;
1470}
1471
1473{
1474 ECS_ASSERT(ecs_is_not_null(ecs));
1475
1476 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
1477 {
1478 ecs_system_t sys = ecs_make_system(sys_id);
1479 ecs_ret_t code = ecs_run_system(ecs, sys, mask);
1480
1481 if (0 != code)
1482 return code;
1483 }
1484
1485 return 0;
1486}
1487
1488/*=============================================================================
1489 * Handle constructors
1490 *============================================================================*/
1491static inline ecs_entity_t ecs_make_entity(ecs_id_t id)
1492{
1493 return (ecs_entity_t){ id };
1494}
1495
1496static inline ecs_comp_t ecs_make_comp(ecs_id_t id)
1497{
1498 return (ecs_comp_t){ id };
1499}
1500
1501static inline ecs_system_t ecs_make_system(ecs_id_t id)
1502{
1503 return (ecs_system_t){ id };
1504}
1505
1506/*=============================================================================
1507 * Realloc wrapper
1508 *============================================================================*/
1509static void* ecs_realloc_zero(ecs_t* ecs, void* ptr, size_t old_size, size_t new_size)
1510{
1511 (void)ecs;
1512
1513 ptr = ECS_REALLOC(ptr, new_size, ecs->mem_ctx);
1514
1515 if (new_size > old_size && ptr) {
1516 size_t diff = new_size - old_size;
1517 void* start = ((char*)ptr)+ old_size;
1518 ECS_MEMSET(start, 0, diff);
1519 }
1520
1521 return ptr;
1522}
1523
1524/*=============================================================================
1525 * Tests if entity is active (created)
1526 *============================================================================*/
1527static inline bool ecs_is_active(ecs_t* ecs, ecs_id_t entity_id)
1528{
1529 ECS_ASSERT(ecs_is_not_null(ecs));
1530 return ecs->entities[entity_id].active;
1531}
1532
1533/*=============================================================================
1534 * Command queue implementation
1535 *============================================================================*/
1536static void ecs_cmd_array_init(ecs_t* ecs, ecs_cmd_array_t* queue, size_t capacity)
1537{
1538 ECS_ASSERT(ecs_is_not_null(ecs));
1539 ECS_ASSERT(ecs_is_not_null(queue));
1540 ECS_ASSERT(capacity > 0);
1541
1542 (void)ecs;
1543
1544 queue->size = 0;
1545 queue->capacity = capacity;
1546 queue->data = (ecs_cmd_t*)ECS_MALLOC(capacity * sizeof(ecs_cmd_t), ecs->mem_ctx);
1547
1548 ECS_ASSERT(ecs_is_not_null(queue->data));
1549}
1550
1551static void ecs_cmd_array_free(ecs_t* ecs, ecs_cmd_array_t* queue)
1552{
1553 ECS_ASSERT(ecs_is_not_null(ecs));
1554 ECS_ASSERT(ecs_is_not_null(queue));
1555 ECS_FREE(queue->data, ecs->mem_ctx);
1556 (void)ecs;
1557 (void)queue;
1558}
1559
1560static ecs_cmd_t* ecs_cmd_array_push(ecs_t* ecs, ecs_cmd_array_t* queue)
1561{
1562 ECS_ASSERT(ecs_is_not_null(ecs));
1563 ECS_ASSERT(ecs_is_not_null(queue));
1564
1565 (void)ecs;
1566
1567 if (queue->size == queue->capacity)
1568 {
1569 size_t new_capacity = queue->capacity * 2;
1570
1571 ECS_ASSERT(ecs_is_valid_capacity(new_capacity, sizeof(ecs_cmd_t)));
1572 queue->data = (ecs_cmd_t*)ECS_REALLOC(queue->data,
1573 new_capacity * sizeof(ecs_cmd_t),
1574 ecs->mem_ctx);
1575 queue->capacity = new_capacity;
1576 }
1577
1578 ecs_cmd_t* cmd = &queue->data[queue->size++];
1579 ECS_MEMSET(cmd, 0, sizeof(ecs_cmd_t));
1580 return cmd;
1581}
1582
1583static void ecs_cmd_flush_queue(ecs_t* ecs)
1584{
1585 ECS_ASSERT(ecs_is_not_null(ecs));
1586
1587 ecs_cmd_array_t* queue = &ecs->cmd_queue;
1588
1589 for (size_t i = 0; i < queue->size; ++i)
1590 {
1591 ecs_cmd_t* cmd = &queue->data[i];
1592
1593 switch (cmd->type)
1594 {
1595 case ECS_CMD_SET:
1596 if (ecs_is_ready(ecs, cmd->entity))
1597 {
1598 ecs_set(ecs, cmd->entity, cmd->comp, cmd->data);
1599 }
1600 break;
1601
1602 case ECS_CMD_ADD:
1603 if (ecs_is_ready(ecs, cmd->entity))
1604 {
1605 ecs_bitset_flip(&ecs->entities[cmd->entity.id].comp_bits, cmd->comp.id, false);
1606 ecs_add(ecs, cmd->entity, cmd->comp, cmd->args);
1607 }
1608 break;
1609
1610 case ECS_CMD_REMOVE:
1611 if (ecs_is_ready(ecs, cmd->entity))
1612 {
1613 ecs_bitset_flip(&ecs->entities[cmd->entity.id].comp_bits, cmd->comp.id, true);
1614 ecs_remove(ecs, cmd->entity, cmd->comp);
1615 }
1616 break;
1617
1618 case ECS_CMD_DESTROY:
1619 if (ecs_is_active(ecs, cmd->entity.id))
1620 {
1621 ecs->entities[cmd->entity.id].ready = true;
1622 ecs_destroy(ecs, cmd->entity);
1623 }
1624 break;
1625 }
1626 }
1627
1628 queue->size = 0;
1629}
1630
1631/*=============================================================================
1632 * Bitset functions
1633 *============================================================================*/
1634
1635#if ECS_MAX_COMPONENTS <= 64
1636
1637static inline bool ecs_bitset_is_zero(ecs_bitset_t* set)
1638{
1639 return *set == 0;
1640}
1641
1642static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on)
1643{
1644 if (on)
1645 *set |= ((uint64_t)1 << bit);
1646 else
1647 *set &= ~((uint64_t)1 << bit);
1648}
1649
1650static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit)
1651{
1652 return *set & ((uint64_t)1 << bit);
1653}
1654
1655static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1, ecs_bitset_t* set2)
1656{
1657 return *set1 & *set2;
1658}
1659
1660static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1, ecs_bitset_t* set2)
1661{
1662 return *set1 | *set2;
1663}
1664
1665static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set)
1666{
1667 return ~(*set);
1668}
1669
1670static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2)
1671{
1672 return *set1 == *set2;
1673}
1674
1675static inline bool ecs_bitset_true(ecs_bitset_t* set)
1676{
1677 return *set;
1678}
1679
1680#else // ECS_MAX_COMPONENTS
1681
1682static inline bool ecs_bitset_is_zero(ecs_bitset_t* set)
1683{
1684 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1685 {
1686 if (set->array[i] != 0)
1687 return false;
1688 }
1689
1690 return true;
1691}
1692
1693static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on)
1694{
1695 int index = bit / ECS_BITSET_WIDTH;
1696
1697 if (on)
1698 set->array[index] |= ((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1699 else
1700 set->array[index] &= ~((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1701}
1702
1703static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit)
1704{
1705 int index = bit / ECS_BITSET_WIDTH;
1706 return set->array[index] & ((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1707}
1708
1709static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1,
1710 ecs_bitset_t* set2)
1711{
1712 ecs_bitset_t set;
1713
1714 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1715 {
1716 set.array[i] = set1->array[i] & set2->array[i];
1717 }
1718
1719 return set;
1720}
1721
1722static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1,
1723 ecs_bitset_t* set2)
1724{
1725 ecs_bitset_t set;
1726
1727 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1728 {
1729 set.array[i] = set1->array[i] | set2->array[i];
1730 }
1731
1732 return set;
1733}
1734
1735static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set)
1736{
1737 ecs_bitset_t out;
1738
1739 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1740 {
1741 out.array[i] = ~set->array[i];
1742 }
1743
1744 return out;
1745}
1746
1747static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2)
1748{
1749 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1750 {
1751 if (set1->array[i] != set2->array[i])
1752 {
1753 return false;
1754 }
1755 }
1756
1757 return true;
1758}
1759
1760static inline bool ecs_bitset_true(ecs_bitset_t* set)
1761{
1762 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1763 {
1764 if (set->array[i])
1765 return true;
1766 }
1767
1768 return false;
1769}
1770
1771#endif // ECS_MAX_COMPONENTS
1772
1773static ecs_arena_block_t* ecs_arena_block_create(ecs_t* ecs, size_t size)
1774{
1775 (void)ecs;
1776
1777 ecs_arena_block_t* block = (ecs_arena_block_t*)ECS_MALLOC(sizeof(ecs_arena_block_t), ecs->mem_ctx);
1778
1779 if (!block)
1780 return NULL;
1781
1782 block->memory = (uint8_t*)ECS_MALLOC(size, ecs->mem_ctx);
1783
1784 if (!block->memory)
1785 {
1786 ECS_FREE(block, ecs->mem_ctx);
1787 return NULL;
1788 }
1789
1790 block->size = size;
1791 block->offset = 0;
1792 block->next = NULL;
1793
1794 return block;
1795}
1796
1797static bool ecs_arena_init(ecs_t* ecs, ecs_arena_t* arena, size_t initial_block_size)
1798{
1799 ecs_arena_block_t* block = ecs_arena_block_create(ecs, initial_block_size);
1800
1801 if (!block)
1802 return false;
1803
1804 arena->first = block;
1805 arena->current = block;
1806 arena->block_size = initial_block_size;
1807
1808 return true;
1809}
1810
1811static bool ecs_arena_grow(ecs_t* ecs, ecs_arena_t* arena, size_t min_size)
1812{
1813 size_t new_size = arena->block_size;
1814
1815 while (new_size < min_size)
1816 {
1817 new_size *= 2;
1818 }
1819
1820 ecs_arena_block_t* block = ecs_arena_block_create(ecs, new_size);
1821
1822 if (!block)
1823 return false;
1824
1825 arena->current->next = block;
1826 arena->current = block;
1827
1828 return true;
1829}
1830
1831static uintptr_t ecs_arena_align_forward(uintptr_t ptr, size_t align)
1832{
1833 uintptr_t mask = (uintptr_t)align - 1;
1834 return (ptr + mask) & ~mask;
1835}
1836
1837static void* ecs_arena_alloc_align(ecs_t* ecs, ecs_arena_t* arena, size_t size, size_t align)
1838{
1839 if ((align & (align - 1)) != 0)
1840 return NULL; // align must be a power of two
1841
1842 ecs_arena_block_t* block = arena->current;
1843
1844 uintptr_t base = (uintptr_t)block->memory;
1845 uintptr_t ptr = base + block->offset;
1846 uintptr_t aligned = ecs_arena_align_forward(ptr, align);
1847 size_t new_offset = (aligned - base) + size;
1848
1849 if (new_offset > block->size)
1850 {
1851 size_t required = size + align;
1852
1853 if (!ecs_arena_grow(ecs, arena, required))
1854 return NULL;
1855
1856 block = arena->current;
1857 base = (uintptr_t)block->memory;
1858 aligned = ecs_arena_align_forward(base, align);
1859 new_offset = (aligned - base) + size;
1860 }
1861
1862 block->offset = new_offset;
1863
1864 return (void*)aligned;
1865}
1866
1867static void* ecs_arena_alloc(ecs_t* ecs, ecs_arena_t* arena, size_t size)
1868{
1869 return ecs_arena_alloc_align(ecs, arena, size, alignof(max_align_t));
1870}
1871
1872static void ecs_arena_reset(ecs_t* ecs, ecs_arena_t* arena)
1873{
1874 (void)ecs;
1875
1876 ecs_arena_block_t* block = arena->first->next;
1877
1878 while (block)
1879 {
1880 ecs_arena_block_t* next = block->next;
1881 ECS_FREE(block->memory, ecs->mem_ctx);
1882 ECS_FREE(block, ecs->mem_ctx);
1883 block = next;
1884 }
1885
1886 arena->first->next = NULL;
1887 arena->first->offset = 0;
1888 arena->current = arena->first;
1889}
1890
1891static void ecs_arena_destroy(ecs_t* ecs, ecs_arena_t* arena)
1892{
1893 (void)ecs;
1894
1895 ecs_arena_block_t* block = arena->first;
1896
1897 while (block)
1898 {
1899 ecs_arena_block_t* next = block->next;
1900 ECS_FREE(block->memory, ecs->mem_ctx);
1901 ECS_FREE(block, ecs->mem_ctx);
1902 block = next;
1903 }
1904
1905 arena->first = NULL;
1906 arena->current = NULL;
1907}
1908
1909/*=============================================================================
1910 * Sparse set functions
1911 *============================================================================*/
1912
1913static void ecs_sparse_set_init(ecs_t* ecs, ecs_sparse_set_t* set, size_t capacity)
1914{
1915 ECS_ASSERT(ecs_is_not_null(ecs));
1916 ECS_ASSERT(ecs_is_not_null(set));
1917
1918 (void)ecs;
1919
1920 set->capacity = capacity;
1921 set->size = 0;
1922
1923 ECS_ASSERT(ecs_is_valid_capacity(capacity, sizeof(ecs_entity_t)));
1924 set->dense = (ecs_entity_t*)ECS_MALLOC(capacity * sizeof(ecs_entity_t), ecs->mem_ctx);
1925
1926 ECS_ASSERT(ecs_is_valid_capacity(capacity, sizeof(size_t)));
1927 set->sparse = (size_t*) ECS_MALLOC(capacity * sizeof(size_t), ecs->mem_ctx);
1928
1929 ECS_MEMSET(set->sparse, 0, capacity * sizeof(size_t));
1930}
1931
1932static void ecs_sparse_set_free(ecs_t* ecs, ecs_sparse_set_t* set)
1933{
1934 ECS_ASSERT(ecs_is_not_null(ecs));
1935 ECS_ASSERT(ecs_is_not_null(set));
1936
1937 (void)ecs;
1938
1939 ECS_FREE(set->dense, ecs->mem_ctx);
1940 ECS_FREE(set->sparse, ecs->mem_ctx);
1941}
1942
1943static bool ecs_sparse_set_add(ecs_t* ecs, ecs_sparse_set_t* set, ecs_id_t id)
1944{
1945 ECS_ASSERT(ecs_is_not_null(ecs));
1946 ECS_ASSERT(ecs_is_not_null(set));
1947 ECS_ASSERT(ecs_is_valid_id(id));
1948
1949 (void)ecs;
1950
1951 // Check if ID exists within the set
1952 if (ecs_sparse_set_find(set, id, NULL))
1953 return false;
1954
1955 // Grow sparse set if necessary
1956 if (id >= set->capacity)
1957 {
1958 size_t old_capacity = set->capacity;
1959 size_t new_capacity = old_capacity;
1960
1961 // Note that since a valid id doesn't have its high bit set, and
1962 // capacity is in terms of elements, doubling the capacity won't wrap
1963 do {
1964 new_capacity *= 2;
1965 } while (id >= new_capacity);
1966
1967
1968 // Grow dense array
1969 ECS_ASSERT(ecs_is_valid_capacity(set->capacity, sizeof(ecs_entity_t)));
1970 set->dense = (ecs_entity_t*)ecs_realloc_zero(ecs,
1971 set->dense,
1972 old_capacity * sizeof(ecs_entity_t),
1973 new_capacity * sizeof(ecs_entity_t));
1974
1975
1976 // Grow sparse array and zero it
1977 ECS_ASSERT(ecs_is_valid_capacity(set->capacity, sizeof(size_t)));
1978 set->sparse = (size_t*)ecs_realloc_zero(ecs,
1979 set->sparse,
1980 old_capacity * sizeof(size_t),
1981 new_capacity * sizeof(size_t));
1982
1983 // Set the new capacity
1984 set->capacity = new_capacity;
1985 }
1986
1987 // Add ID to set
1988 set->dense[set->size].id = id;
1989 set->sparse[id] = set->size;
1990 set->size++;
1991
1992 return true;
1993}
1994
1995static inline bool ecs_sparse_set_find(ecs_sparse_set_t* set, ecs_id_t id, size_t* found)
1996{
1997 ECS_ASSERT(ecs_is_not_null(set));
1998 ECS_ASSERT(ecs_is_valid_id(id));
1999
2000 if (id < set->capacity && set->sparse[id] < set->size && set->dense[set->sparse[id]].id == id)
2001 {
2002 if (found) *found = set->sparse[id];
2003 return true;
2004 }
2005 else
2006 {
2007 if (found) *found = 0;
2008 return false;
2009 }
2010}
2011
2012static inline bool ecs_sparse_set_remove(ecs_sparse_set_t* set, ecs_id_t id)
2013{
2014 ECS_ASSERT(ecs_is_not_null(set));
2015 ECS_ASSERT(ecs_is_valid_id(id));
2016
2017 if (!ecs_sparse_set_find(set, id, NULL))
2018 return false;
2019
2020 // Swap and remove (changes order of array)
2021 ecs_id_t tmp = set->dense[set->size - 1].id;
2022 set->dense[set->sparse[id]].id = tmp;
2023 set->sparse[tmp] = set->sparse[id];
2024
2025 set->size--;
2026
2027 return true;
2028}
2029
2030/*=============================================================================
2031 * System entity add/remove functions
2032 *============================================================================*/
2033#if ECS_MAX_COMPONENTS <= 64
2034
2035static inline bool ecs_entity_system_test(ecs_bitset_t require_bits,
2036 ecs_bitset_t exclude_bits,
2037 ecs_bitset_t entity_bits)
2038{
2039 if (entity_bits & exclude_bits)
2040 return false;
2041
2042 if ((entity_bits & require_bits) != require_bits)
2043 return false;
2044
2045 return true;
2046}
2047
2048#else // ECS_MAX_COMPONENTS
2049
2050static inline bool ecs_entity_system_test(ecs_bitset_t require_bits,
2051 ecs_bitset_t exclude_bits,
2052 ecs_bitset_t entity_bits)
2053{
2054 if (!ecs_bitset_is_zero(&exclude_bits))
2055 {
2056 ecs_bitset_t overlap = ecs_bitset_and(&entity_bits, &exclude_bits);
2057
2058 if (ecs_bitset_true(&overlap))
2059 {
2060 return false;
2061 }
2062 }
2063
2064 ecs_bitset_t entity_and_require = ecs_bitset_and(&entity_bits, &require_bits);
2065 return ecs_bitset_equal(&entity_and_require, &require_bits);
2066}
2067#endif // ECS_MAX_COMPONENTS
2068
2069static void ecs_sync_add_remove(ecs_t* ecs, ecs_id_t entity_id, ecs_id_t comp_id)
2070{
2071 // Load entity data
2072 ecs_entity_data_t* entity_data = &ecs->entities[entity_id];
2073
2074 // Add or remove entity from systems
2075 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
2076 {
2077 ecs_sys_data_t* sys_data = &ecs->systems[sys_id];
2078
2079 // Skip systems that don't reference the changed component --
2080 // their match result cannot have changed
2081 if (!ecs_bitset_test(&sys_data->require_bits, comp_id) &&
2082 !ecs_bitset_test(&sys_data->exclude_bits, comp_id))
2083 continue;
2084
2085 // Test to see if entity's components matches the system
2086 if (ecs_entity_system_test(sys_data->require_bits,
2087 sys_data->exclude_bits,
2088 entity_data->comp_bits))
2089 {
2090 // Add the entity directly to the sparse set
2091 if (ecs_sparse_set_add(ecs, &sys_data->entity_ids, entity_id))
2092 {
2093 if (sys_data->on_join)
2094 sys_data->on_join(ecs, ecs_make_entity(entity_id), sys_data->udata);
2095 }
2096 }
2097 else
2098 {
2099 // Just remove the entity from the sparse set if its components
2100 // no longer match
2101 if (ecs_sparse_set_remove(&sys_data->entity_ids, entity_id))
2102 {
2103 if (sys_data->on_leave)
2104 sys_data->on_leave(ecs, ecs_make_entity(entity_id), sys_data->udata);
2105 }
2106 }
2107 }
2108}
2109
2110static void ecs_sync_destroy(ecs_t* ecs, ecs_id_t entity_id)
2111{
2112 // Remove entity from systems
2113 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
2114 {
2115 ecs_sys_data_t* sys_data = &ecs->systems[sys_id];
2116
2117 if (ecs_sparse_set_remove(&sys_data->entity_ids, entity_id))
2118 {
2119 if (sys_data->on_leave)
2120 sys_data->on_leave(ecs, ecs_make_entity(entity_id), sys_data->udata);
2121 }
2122 }
2123}
2124
2125/*=============================================================================
2126 * ID array functions
2127 *============================================================================*/
2128
2129static void ecs_id_array_init(ecs_t* ecs, ecs_id_array_t* array, size_t capacity)
2130{
2131 ECS_ASSERT(ecs_is_not_null(ecs));
2132 ECS_ASSERT(ecs_is_not_null(array));
2133
2134 (void)ecs;
2135
2136 array->size = 0;
2137 array->capacity = capacity;
2138
2139 ECS_ASSERT(ecs_is_valid_capacity(capacity, sizeof(ecs_id_t)));
2140 array->data = (ecs_id_t*)ECS_MALLOC(capacity * sizeof(ecs_id_t), ecs->mem_ctx);
2141}
2142
2143static void ecs_id_array_free(ecs_t* ecs, ecs_id_array_t* array)
2144{
2145 ECS_ASSERT(ecs_is_not_null(ecs));
2146 ECS_ASSERT(ecs_is_not_null(array));
2147
2148 (void)ecs;
2149
2150 ECS_FREE(array->data, ecs->mem_ctx);
2151}
2152
2153static inline void ecs_id_array_push(ecs_t* ecs, ecs_id_array_t* array, ecs_id_t id)
2154{
2155 ECS_ASSERT(ecs_is_not_null(ecs));
2156 ECS_ASSERT(ecs_is_not_null(array));
2157 ECS_ASSERT(ecs_is_valid_id(id));
2158
2159 (void)ecs;
2160
2161 if (array->size == array->capacity)
2162 {
2163
2164 // Note that since a valid id doesn't have its high bit set, and
2165 // capacity is in terms of elements, doubling the capacity won't wrap
2166 array->capacity *= 2;
2167
2168 ECS_ASSERT(ecs_is_valid_capacity(array->capacity, sizeof(ecs_id_t)));
2169 array->data = (ecs_id_t*)ECS_REALLOC(array->data,
2170 array->capacity * sizeof(ecs_id_t),
2171 ecs->mem_ctx);
2172 }
2173
2174 array->data[array->size++] = id;
2175}
2176
2177static inline ecs_id_t ecs_id_array_pop(ecs_id_array_t* array)
2178{
2179 ECS_ASSERT(ecs_is_not_null(array));
2180 ECS_ASSERT(array->size > 0);
2181
2182 return array->data[--array->size];
2183}
2184
2185static inline size_t ecs_id_array_size(ecs_id_array_t* array)
2186{
2187 return array->size;
2188}
2189
2190static void ecs_comp_blocks_init(ecs_t* ecs, ecs_comp_blocks_t* array, size_t size, size_t capacity)
2191{
2192 ECS_ASSERT(ecs_is_not_null(ecs));
2193 ECS_ASSERT(ecs_is_not_null(array));
2194
2195 (void)ecs;
2196
2197 ECS_MEMSET(array, 0, sizeof(ecs_comp_blocks_t));
2198
2199 array->comp_size = size;
2200
2201 size_t initial_blocks = (capacity + ECS_COMP_BLOCK_SIZE - 1) / ECS_COMP_BLOCK_SIZE;
2202 if (initial_blocks == 0) initial_blocks = 1;
2203
2204 array->block_capacity = initial_blocks;
2205 array->block_count = initial_blocks;
2206
2207 ECS_ASSERT(ecs_is_valid_capacity(initial_blocks, sizeof(void*)));
2208 array->blocks = (void**)ECS_MALLOC(initial_blocks * sizeof(void*), ecs->mem_ctx);
2209
2210 for (size_t i = 0; i < initial_blocks; i++)
2211 {
2212 ECS_ASSERT(ecs_is_valid_capacity(ECS_COMP_BLOCK_SIZE, size));
2213 array->blocks[i] = ECS_MALLOC(ECS_COMP_BLOCK_SIZE * size, ecs->mem_ctx);
2214 ECS_MEMSET(array->blocks[i], 0, ECS_COMP_BLOCK_SIZE * size);
2215 }
2216}
2217
2218static void ecs_comp_blocks_free(ecs_t* ecs, ecs_comp_blocks_t* array)
2219{
2220 ECS_ASSERT(ecs_is_not_null(ecs));
2221 ECS_ASSERT(ecs_is_not_null(array));
2222
2223 (void)ecs;
2224
2225 for (size_t i = 0; i < array->block_count; i++)
2226 {
2227 ECS_FREE(array->blocks[i], ecs->mem_ctx);
2228 }
2229
2230 ECS_FREE(array->blocks, ecs->mem_ctx);
2231}
2232
2233static void ecs_comp_blocks_resize(ecs_t* ecs, ecs_comp_blocks_t* array, ecs_id_t id)
2234{
2235 ECS_ASSERT(ecs_is_not_null(ecs));
2236 ECS_ASSERT(ecs_is_not_null(array));
2237 ECS_ASSERT(ecs_is_valid_id(id));
2238
2239 size_t required_block = id / ECS_COMP_BLOCK_SIZE;
2240
2241 while (required_block >= array->block_count)
2242 {
2243 // Grow the block pointer array if necessary. This only moves pointers,
2244 // never the block data itself, so existing component pointers remain valid.
2245 if (array->block_count == array->block_capacity)
2246 {
2247 size_t old_capacity = array->block_capacity;
2248 size_t new_capacity = old_capacity * 2;
2249
2250 ECS_ASSERT(ecs_is_valid_capacity(array->block_capacity, sizeof(void*)));
2251 array->blocks = (void**)ecs_realloc_zero(ecs,
2252 array->blocks,
2253 old_capacity * sizeof(void*),
2254 new_capacity * sizeof(void*));
2255
2256
2257 array->block_capacity = new_capacity;
2258 }
2259
2260 // Allocate and zero a new block
2261 ECS_ASSERT(ecs_is_valid_capacity(ECS_COMP_BLOCK_SIZE, array->comp_size));
2262 void* block = ECS_MALLOC(ECS_COMP_BLOCK_SIZE * array->comp_size, ecs->mem_ctx);
2263 ECS_MEMSET(block, 0, ECS_COMP_BLOCK_SIZE * array->comp_size);
2264 array->blocks[array->block_count++] = block;
2265 }
2266}
2267
2268/*=============================================================================
2269 * Validation functions
2270 *============================================================================*/
2271#ifndef NDEBUG
2272static bool ecs_is_not_null(void* ptr)
2273{
2274 return NULL != ptr;
2275}
2276
2277static bool ecs_is_valid_component_id(ecs_id_t id)
2278{
2279 return id < ECS_MAX_COMPONENTS;
2280}
2281
2282static bool ecs_is_valid_system_id(ecs_id_t id)
2283{
2284 return id < ECS_MAX_SYSTEMS;
2285}
2286
2287static bool ecs_is_valid_id(ecs_id_t id)
2288{
2289 // Ensures high bit is not set - works for any unsigned ecs_id_t
2290 return id == ((id << 1) >> 1);
2291}
2292
2293static bool ecs_is_valid_capacity(size_t capacity, size_t elem_size)
2294{
2295 // Ensures any array allocations won't overflow a signed size_t and are
2296 // nonzero. This is not the most efficient implementation, but it is simple
2297
2298 if (capacity == 0 || elem_size == 0)
2299 {
2300 return false;
2301 }
2302
2303 size_t max_cap = (SIZE_MAX >> 1) / elem_size;
2304 return capacity <= max_cap;
2305}
2306
2307static bool ecs_is_entity_ready(ecs_t* ecs, ecs_id_t entity_id)
2308{
2309 return ecs->entities[entity_id].ready;
2310}
2311
2312static bool ecs_is_component_ready(ecs_t* ecs, ecs_id_t comp_id)
2313{
2314 return comp_id < ecs->comp_count;
2315}
2316
2317static bool ecs_is_system_ready(ecs_t* ecs, ecs_id_t sys_id)
2318{
2319 return sys_id < ecs->system_count;
2320}
2321
2322#endif // NDEBUG
2323
2324#endif // PICO_ECS_IMPLEMENTATION
2325
2326/*
2327 ----------------------------------------------------------------------------
2328 This software is available under two licenses (A) or (B). You may choose
2329 either one as you wish:
2330 ----------------------------------------------------------------------------
2331
2332 (A) The zlib License
2333
2334 Copyright (c) 2025 James McLean
2335
2336 This software is provided 'as-is', without any express or implied warranty.
2337 In no event will the authors be held liable for any damages arising from the
2338 use of this software.
2339
2340 Permission is granted to anyone to use this software for any purpose,
2341 including commercial applications, and to alter it and redistribute it
2342 freely, subject to the following restrictions:
2343
2344 1. The origin of this software must not be misrepresented; you must not
2345 claim that you wrote the original software. If you use this software in a
2346 product, an acknowledgment in the product documentation would be appreciated
2347 but is not required.
2348
2349 2. Altered source versions must be plainly marked as such, and must not be
2350 misrepresented as being the original software.
2351
2352 3. This notice may not be removed or altered from any source distribution.
2353
2354 ----------------------------------------------------------------------------
2355
2356 (B) Public Domain (www.unlicense.org)
2357
2358 This is free and unencumbered software released into the public domain.
2359
2360 Anyone is free to copy, modify, publish, use, compile, sell, or distribute
2361 this software, either in source code form or as a compiled binary, for any
2362 purpose, commercial or non-commercial, and by any means.
2363
2364 In jurisdictions that recognize copyright laws, the author or authors of
2365 this software dedicate any and all copyright interest in the software to the
2366 public domain. We make this dedication for the benefit of the public at
2367 large and to the detriment of our heirs and successors. We intend this
2368 dedication to be an overt act of relinquishment in perpetuity of all present
2369 and future rights to this software under copyright law.
2370
2371 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2372 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2373 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2374 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2375 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2376 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2377*/
2378
2379// EoF
void ecs_enable_system(ecs_t *ecs, ecs_system_t sys)
Enables a system.
#define ECS_INVALID_ID
An invalid ID.
Definition pico_ecs.h:226
#define ECS_MASK_TYPE
Determine mask type.
Definition pico_ecs.h:195
void * ecs_get(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp)
Gets a component instance associated with an entity.
void(* ecs_on_leave_fn)(ecs_t *ecs, ecs_entity_t entity, void *udata)
Called when an entity is removed from a system.
Definition pico_ecs.h:365
void(* ecs_on_join_fn)(ecs_t *ecs, ecs_entity_t entity, void *udata)
Called when an entity is added to a system.
Definition pico_ecs.h:356
void ecs_remove(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp)
Removes a component instance from an entity.
ECS_MASK_TYPE ecs_mask_t
Type for value used in system matching.
Definition pico_ecs.h:201
ecs_ret_t ecs_run_systems(ecs_t *ecs, ecs_mask_t mask)
Updates all systems.
ECS_ID_TYPE ecs_id_t
ID used for entity and components.
Definition pico_ecs.h:189
void ecs_set(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *data)
Copies data into a component instance associated with an entity.
ecs_ret_t(* ecs_system_fn)(ecs_t *ecs, ecs_entity_t *entities, size_t entity_count, void *udata)
System callback.
Definition pico_ecs.h:344
void ecs_destroy(ecs_t *ecs, ecs_entity_t entity)
Destroys an entity.
ecs_comp_t ecs_define_component(ecs_t *ecs, size_t size, const ecs_comp_desc_t *desc)
Defines a component.
void(* ecs_on_remove_fn)(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *udata)
Called when a component is destroyed (via ecs_remove or ecs_destroy)
Definition pico_ecs.h:275
void ecs_free(ecs_t *ecs)
Destroys an ECS context.
void * ecs_get_system_udata(ecs_t *ecs, ecs_system_t sys)
Gets the user data from a system.
void ecs_set_system_udata(ecs_t *ecs, ecs_system_t sys, void *udata)
Sets the user data for a system.
struct ecs_s ecs_t
ECS context.
Definition pico_ecs.h:177
void ecs_reset(ecs_t *ecs)
Removes all entities from the ECS, preserving systems and components.
void ecs_set_system_callbacks(ecs_t *ecs, ecs_system_t sys, ecs_system_fn system_cb, ecs_on_join_fn on_join, ecs_on_leave_fn on_leave)
Updates the callbacks for an existing system.
bool ecs_is_ready(ecs_t *ecs, ecs_entity_t entity)
Returns true if the entity is currently active and has not been queued for destruction.
void(* ecs_on_add_fn)(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, const void *args, void *udata)
Called when a component is created (via ecs_add)
Definition pico_ecs.h:262
void(* ecs_on_set_fn)(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *udata)
Called when a component's data is set (via ecs_set)
Definition pico_ecs.h:289
ecs_ret_t ecs_run_system(ecs_t *ecs, ecs_system_t sys, ecs_mask_t mask)
Update an individual system.
ecs_entity_t * ecs_get_entity_array(ecs_t *ecs, ecs_system_t sys)
Returns the entities associated with the specified system.
ecs_t * ecs_new(size_t entity_capacity, void *mem_ctx)
Creates an ECS context.
int32_t ecs_ret_t
Return code for system callback and calling functions.
Definition pico_ecs.h:206
void ecs_set_system_mask(ecs_t *ecs, ecs_system_t sys, ecs_mask_t mask)
Sets the system's mask.
size_t ecs_get_entity_count(ecs_t *ecs, ecs_system_t sys)
Returns the number of entities assigned to the specified system.
void ecs_disable_system(ecs_t *ecs, ecs_system_t sys)
Disables a system.
bool ecs_has(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp)
Test if entity has the specified component.
ecs_entity_t ecs_create(ecs_t *ecs)
Creates an entity.
#define ECS_ID_TYPE
Determine ID type. It should be unsigned.
Definition pico_ecs.h:183
void ecs_add(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *args)
Adds a component instance to an entity.
void ecs_exclude(ecs_t *ecs, ecs_system_t sys, ecs_comp_t comp)
Excludes entities having the specified component from being added to the target system.
void ecs_require(ecs_t *ecs, ecs_system_t sys, ecs_comp_t comp)
Entities are processed by the target system if they have all of the the components required by the sy...
ecs_system_t ecs_define_system(ecs_t *ecs, ecs_system_fn system_cb, const ecs_sys_desc_t *desc)
Defines a system.
ecs_mask_t ecs_get_system_mask(ecs_t *ecs, ecs_system_t sys)
Returns the system mask.
Optional parameters for component definition.
Definition pico_ecs.h:309
ecs_on_remove_fn on_remove_cb
Definition pico_ecs.h:311
size_t args_size
Definition pico_ecs.h:314
ecs_on_add_fn on_add_cb
Definition pico_ecs.h:310
ecs_on_set_fn on_set_cb
Definition pico_ecs.h:312
void * udata
Definition pico_ecs.h:315
void * default_value
Definition pico_ecs.h:313
A component handle.
Definition pico_ecs.h:216
ecs_id_t id
Definition pico_ecs.h:216
An entity handle.
Definition pico_ecs.h:211
ecs_id_t id
Definition pico_ecs.h:211
Optional parameters for system definition.
Definition pico_ecs.h:377
void * udata
Definition pico_ecs.h:381
ecs_on_join_fn on_join_cb
Definition pico_ecs.h:379
ecs_on_leave_fn on_leave_cb
Definition pico_ecs.h:380
ecs_mask_t mask
Definition pico_ecs.h:378
A system handle.
Definition pico_ecs.h:221
ecs_id_t id
Definition pico_ecs.h:221