Pico Headers
Loading...
Searching...
No Matches
pico_ecs.h
Go to the documentation of this file.
1
145#ifndef PICO_ECS_H
146#define PICO_ECS_H
147
148#include <stdbool.h> // bool, true, false
149#include <stddef.h> // size_t
150#include <stdint.h> // uint32_t
151
152#ifdef __cplusplus
153extern "C" {
154#endif
155
159typedef struct ecs_s ecs_t;
160
164#ifndef ECS_ID_TYPE
165#define ECS_ID_TYPE uint64_t
166#endif
167
172
176#ifndef ECS_MASK_TYPE
177#define ECS_MASK_TYPE uint64_t
178#endif
179
184
188typedef int32_t ecs_ret_t;
189
194
198typedef struct ecs_comp_t { ecs_id_t id; } ecs_comp_t;
199
204
209
214
223ecs_t* ecs_new(size_t entity_count, void* mem_ctx);
224
230void ecs_free(ecs_t* ecs);
231
235void ecs_reset(ecs_t* ecs);
236
244typedef void (*ecs_constructor_fn)(ecs_t* ecs,
245 ecs_entity_t entity,
246 void* comp_ptr,
247 void* args);
248
256typedef void (*ecs_destructor_fn)(ecs_t* ecs,
257 ecs_entity_t entity,
258 void* comp_ptr);
259
274 size_t size,
275 ecs_constructor_fn constructor,
276 ecs_destructor_fn destructor);
277
290 ecs_entity_t* entities,
291 size_t entity_count,
292 void* udata);
293
301typedef void (*ecs_added_fn)(ecs_t* ecs, ecs_entity_t entity, void* udata);
302
310typedef void (*ecs_removed_fn)(ecs_t* ecs, ecs_entity_t entity, void* udata);
311
328 ecs_mask_t mask,
329 ecs_system_fn system_cb,
330 ecs_added_fn add_cb,
331 ecs_removed_fn remove_cb,
332 void* udata);
342
352
360
368
379 ecs_system_t sys,
380 ecs_system_fn system_cb,
381 ecs_added_fn add_cb,
382 ecs_removed_fn remove_cb);
383
391void ecs_set_system_udata(ecs_t* ecs, ecs_system_t sys, void* udata);
392
401
410
419
424
433
441bool ecs_is_ready(ecs_t* ecs, ecs_entity_t entity);
442
452bool ecs_has(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp);
453
463void* ecs_add(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp, void* args);
464
474void* ecs_get(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp);
475
489void ecs_destroy(ecs_t* ecs, ecs_entity_t entity);
490
503void ecs_remove(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp);
504
514
526
537
549
550#ifdef __cplusplus
551}
552#endif
553
554#endif // PICO_ECS_H
555
556#ifdef PICO_ECS_IMPLEMENTATION // Define once
557
558#include <stddef.h> // size_t
559#include <stdint.h> // uint32_t, uint64_t
560#include <stdlib.h> // malloc, realloc, free
561#include <string.h> // memcpy, memset
562
563#ifndef PICO_ECS_MAX_COMPONENTS
564#define PICO_ECS_MAX_COMPONENTS 32
565#endif
566
567#ifndef PICO_ECS_MAX_SYSTEMS
568#define PICO_ECS_MAX_SYSTEMS 16
569#endif
570
571#ifdef NDEBUG
572 #define PICO_ECS_ASSERT(expr) ((void)0)
573#else
574 #ifndef PICO_ECS_ASSERT
575 #include <assert.h>
576 #define PICO_ECS_ASSERT(expr) (assert(expr))
577 #endif
578#endif
579
580#if !defined(PICO_ECS_MALLOC) || !defined(PICO_ECS_REALLOC) || !defined(PICO_ECS_FREE)
581#include <stdlib.h>
582#define PICO_ECS_MALLOC(size, ctx) (malloc(size))
583#define PICO_ECS_REALLOC(ptr, size, ctx) (realloc(ptr, size))
584#define PICO_ECS_FREE(ptr, ctx) (free(ptr))
585#endif
586
587/*=============================================================================
588 * Aliases
589 *============================================================================*/
590
591#define ECS_ASSERT PICO_ECS_ASSERT
592#define ECS_MAX_COMPONENTS PICO_ECS_MAX_COMPONENTS
593#define ECS_MAX_SYSTEMS PICO_ECS_MAX_SYSTEMS
594#define ECS_MALLOC PICO_ECS_MALLOC
595#define ECS_REALLOC PICO_ECS_REALLOC
596#define ECS_FREE PICO_ECS_FREE
597
598/*=============================================================================
599 * Data structures
600 *============================================================================*/
601
602#if ECS_MAX_COMPONENTS <= 32
603typedef uint32_t ecs_bitset_t;
604#elif ECS_MAX_COMPONENTS <= 64
605typedef uint64_t ecs_bitset_t;
606#else
607#define ECS_BITSET_WIDTH 64
608#define ECS_BITSET_SIZE (((ECS_MAX_COMPONENTS - 1) / ECS_BITSET_WIDTH) + 1)
609
610typedef struct
611{
612 uint64_t array[ECS_BITSET_SIZE];
613} ecs_bitset_t;
614
615#endif // ECS_MAX_COMPONENTS
616
617// Data-structure for a packed array implementation that provides O(1) functions
618// for adding, removing, and accessing entity IDs
619typedef struct
620{
621 size_t capacity;
622 size_t size;
623 size_t* sparse;
624 ecs_entity_t* dense;
625} ecs_sparse_set_t;
626
627// A data-structure for providing O(1) operations for working with IDs
628typedef struct
629{
630 size_t capacity;
631 size_t size; // array size
632 ecs_id_t* data;
633} ecs_id_array_t;
634
635typedef struct
636{
637 size_t capacity;
638 size_t size; // component size
639 void* data;
640} ecs_comp_array_t;
641
642typedef struct
643{
644 ecs_bitset_t comp_bits;
645 bool active;
646 bool ready;
647} ecs_entity_data_t;
648
649typedef struct
650{
651 ecs_constructor_fn constructor;
652 ecs_destructor_fn destructor;
653} ecs_comp_data_t;
654
655typedef struct
656{
657 bool active;
658 ecs_sparse_set_t entity_ids;
659 ecs_mask_t mask;
660 ecs_system_fn system_cb;
661 ecs_added_fn add_cb;
662 ecs_removed_fn remove_cb;
663 ecs_bitset_t require_bits;
664 ecs_bitset_t exclude_bits;
665 void* udata;
666} ecs_sys_data_t;
667
668struct ecs_s
669{
670 ecs_id_array_t entity_pool;
671 ecs_id_array_t destroy_queue;
672 ecs_id_array_t remove_queue;
673 ecs_entity_data_t* entities;
674 size_t entity_count;
675 size_t next_entity_id;
676 ecs_comp_data_t comps[ECS_MAX_COMPONENTS];
677 ecs_comp_array_t comp_arrays[ECS_MAX_COMPONENTS];
678 size_t comp_count;
679 ecs_sys_data_t systems[ECS_MAX_SYSTEMS];
680 size_t system_count;
681 void* mem_ctx;
682};
683
684/*=============================================================================
685 * Handle constructors
686 *============================================================================*/
687static inline ecs_entity_t ecs_make_entity(ecs_id_t id);
688static inline ecs_comp_t ecs_make_comp(ecs_id_t id);
689static inline ecs_system_t ecs_make_system(ecs_id_t id);
690
691/*=============================================================================
692 * Realloc wrapper
693 *============================================================================*/
694static void* ecs_realloc_zero(ecs_t* ecs, void* ptr, size_t old_size, size_t new_size);
695
696/*=============================================================================
697 * Removes entity from ALL systems
698 *============================================================================*/
699static void ecs_remove_from_systems(ecs_t* ecs, ecs_entity_t entity);
700
701/*=============================================================================
702 * Calls destructors on all components of the entity
703 *============================================================================*/
704static void ecs_destruct(ecs_t* ecs, ecs_id_t entity);
705
706/*=============================================================================
707 * Tests if entity is active (created)
708 *============================================================================*/
709static inline bool ecs_is_active(ecs_t* ecs, ecs_id_t entity_id);
710
711/*=============================================================================
712 * Functions to flush destroyed entities and removed component
713 *============================================================================*/
714static void ecs_flush_destroyed(ecs_t* ecs);
715static void ecs_flush_removed(ecs_t* ecs);
716
717/*=============================================================================
718 * Bitset functions
719 *============================================================================*/
720static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on);
721static inline bool ecs_bitset_is_zero(ecs_bitset_t* set);
722static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit);
723static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1, ecs_bitset_t* set2);
724static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1, ecs_bitset_t* set2);
725static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set);
726static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2);
727static inline bool ecs_bitset_true(ecs_bitset_t* set);
728
729/*=============================================================================
730 * Sparse set functions
731 *============================================================================*/
732static void ecs_sparse_set_init(ecs_t* ecs, ecs_sparse_set_t* set, size_t capacity);
733static void ecs_sparse_set_free(ecs_t* ecs, ecs_sparse_set_t* set);
734static bool ecs_sparse_set_add(ecs_t* ecs, ecs_sparse_set_t* set, ecs_id_t id);
735static bool ecs_sparse_set_find(ecs_sparse_set_t* set, ecs_id_t id, size_t* found);
736static bool ecs_sparse_set_remove(ecs_sparse_set_t* set, ecs_id_t id);
737
738/*=============================================================================
739 * System entity add/remove functions
740 *============================================================================*/
741static bool ecs_entity_system_test(ecs_bitset_t* require_bits,
742 ecs_bitset_t* exclude_bits,
743 ecs_bitset_t* entity_bits);
744
745/*=============================================================================
746 * ID array functions
747 *============================================================================*/
748static void ecs_id_array_init(ecs_t* ecs, ecs_id_array_t* pool, int capacity);
749static void ecs_id_array_free(ecs_t* ecs, ecs_id_array_t* pool);
750static void ecs_id_array_push(ecs_t* ecs, ecs_id_array_t* pool, ecs_id_t id);
751static ecs_id_t ecs_id_array_pop(ecs_id_array_t* pool);
752static int ecs_id_array_size(ecs_id_array_t* pool);
753
754/*=============================================================================
755 * Component array functions
756 *============================================================================*/
757static void ecs_comp_array_init(ecs_t* ecs, ecs_comp_array_t* array, size_t size, size_t capacity);
758static void ecs_comp_array_free(ecs_t* ecs, ecs_comp_array_t* array);
759static void ecs_comp_array_resize(ecs_t* ecs, ecs_comp_array_t* array, size_t capacity);
760
761/*=============================================================================
762 * Validation functions
763 *============================================================================*/
764#ifndef NDEBUG
765static bool ecs_is_not_null(void* ptr);
766static bool ecs_is_valid_component_id(ecs_id_t id);
767static bool ecs_is_valid_system_id(ecs_id_t id);
768static bool ecs_is_entity_ready(ecs_t* ecs, ecs_id_t entity_id);
769static bool ecs_is_component_ready(ecs_t* ecs, ecs_id_t comp_id);
770static bool ecs_is_system_ready(ecs_t* ecs, ecs_id_t sys_id);
771#endif // NDEBUG
772
773/*=============================================================================
774 * Public API implementation
775 *============================================================================*/
776
778{
779 return 0 == entity.id;
780}
781
783{
784 ecs_entity_t invalid = { 0 };
785 return invalid;
786}
787
788ecs_t* ecs_new(size_t entity_count, void* mem_ctx)
789{
790 ECS_ASSERT(entity_count > 0);
791
792 ecs_t* ecs = (ecs_t*)ECS_MALLOC(sizeof(ecs_t), mem_ctx);
793
794 // Out of memory
795 if (NULL == ecs)
796 return NULL;
797
798 memset(ecs, 0, sizeof(ecs_t));
799
800 ecs->entity_count = (entity_count > 0) ? entity_count : 1;
801 ecs->next_entity_id = 1;
802 ecs->mem_ctx = mem_ctx;
803
804 // Initialize entity pool and queues
805 ecs_id_array_init(ecs, &ecs->entity_pool, entity_count);
806 ecs_id_array_init(ecs, &ecs->destroy_queue, entity_count);
807 ecs_id_array_init(ecs, &ecs->remove_queue, entity_count * 2);
808
809 // Allocate entity array
810 ecs->entities = (ecs_entity_data_t*)ECS_MALLOC(ecs->entity_count * sizeof(ecs_entity_data_t),
811 ecs->mem_ctx);
812
813 // Zero entity array
814 memset(ecs->entities, 0, ecs->entity_count * sizeof(ecs_entity_data_t));
815
816 return ecs;
817}
818
819void ecs_free(ecs_t* ecs)
820{
821 ECS_ASSERT(ecs_is_not_null(ecs));
822
823 for (ecs_id_t entity_id = 0; entity_id < ecs->entity_count; entity_id++)
824 {
825 if (ecs->entities[entity_id].active)
826 ecs_destruct(ecs, entity_id);
827 }
828
829 ecs_id_array_free(ecs, &ecs->entity_pool);
830 ecs_id_array_free(ecs, &ecs->destroy_queue);
831 ecs_id_array_free(ecs, &ecs->remove_queue);
832
833 for (ecs_id_t comp_id = 0; comp_id < ecs->comp_count; comp_id++)
834 {
835 ecs_comp_array_t* comp_array = &ecs->comp_arrays[comp_id];
836 ecs_comp_array_free(ecs, comp_array);
837 }
838
839 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
840 {
841 ecs_sys_data_t* sys = &ecs->systems[sys_id];
842 ecs_sparse_set_free(ecs, &sys->entity_ids);
843 }
844
845 ECS_FREE(ecs->entities, ecs->mem_ctx);
846 ECS_FREE(ecs, ecs->mem_ctx);
847}
848
849void ecs_reset(ecs_t* ecs)
850{
851 ECS_ASSERT(ecs_is_not_null(ecs));
852
853 for (ecs_id_t entity_id = 0; entity_id < ecs->entity_count; entity_id++)
854 {
855 if (ecs->entities[entity_id].active)
856 ecs_destruct(ecs, entity_id);
857 }
858
859 ecs->entity_pool.size = 0;
860 ecs->destroy_queue.size = 0;
861 ecs->remove_queue.size = 0;
862
863 memset(ecs->entities, 0, ecs->entity_count * sizeof(ecs_entity_data_t));
864
865 ecs->next_entity_id = 1;
866
867 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
868 {
869 ecs->systems[sys_id].entity_ids.size = 0;
870 }
871}
872
874 size_t size,
875 ecs_constructor_fn constructor,
876 ecs_destructor_fn destructor)
877{
878 ECS_ASSERT(ecs_is_not_null(ecs));
879 ECS_ASSERT(ecs->comp_count < ECS_MAX_COMPONENTS);
880 ECS_ASSERT(size > 0);
881
882 ecs_comp_t comp = ecs_make_comp(ecs->comp_count);
883
884 ecs_comp_array_t* comp_array = &ecs->comp_arrays[comp.id];
885 ecs_comp_array_init(ecs, comp_array, size, ecs->entity_count);
886
887 ecs->comps[comp.id].constructor = constructor;
888 ecs->comps[comp.id].destructor = destructor;
889
890 ecs->comp_count++;
891
892 return comp;
893}
894
896 ecs_mask_t mask,
897 ecs_system_fn system_cb,
898 ecs_added_fn add_cb,
899 ecs_removed_fn remove_cb,
900 void* udata)
901{
902 ECS_ASSERT(ecs_is_not_null(ecs));
903 ECS_ASSERT(ecs->system_count < ECS_MAX_SYSTEMS);
904 ECS_ASSERT(NULL != system_cb);
905
906 ecs_system_t sys = ecs_make_system(ecs->system_count);
907 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
908
909 ecs_sparse_set_init(ecs, &sys_data->entity_ids, ecs->entity_count);
910
911 sys_data->active = true;
912 sys_data->mask = mask;
913 sys_data->system_cb = system_cb;
914 sys_data->add_cb = add_cb;
915 sys_data->remove_cb = remove_cb;
916 sys_data->udata = udata;
917
918 ecs->system_count++;
919
920 return sys;
921}
922
924{
925 ECS_ASSERT(ecs_is_not_null(ecs));
926 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
927 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
928 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
929 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
930
931 // Set system component bit for the specified component
932 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
933 ecs_bitset_flip(&sys_data->require_bits, comp.id, true);
934}
935
937{
938 ECS_ASSERT(ecs_is_not_null(ecs));
939 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
940 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
941 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
942 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
943
944 // Set system component bit for the specified component
945 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
946 ecs_bitset_flip(&sys_data->exclude_bits, comp.id, true);
947}
948
950{
951 ECS_ASSERT(ecs_is_not_null(ecs));
952 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
953 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
954
955 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
956 sys_data->active = true;
957}
958
960{
961 ECS_ASSERT(ecs_is_not_null(ecs));
962 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
963 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
964
965 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
966 sys_data->active = false;
967}
968
970 ecs_system_t sys,
971 ecs_system_fn system_cb,
972 ecs_added_fn add_cb,
973 ecs_removed_fn remove_cb)
974{
975 ECS_ASSERT(ecs_is_not_null(ecs));
976 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
977 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
978 ECS_ASSERT(NULL != system_cb);
979
980 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
981 sys_data->system_cb = system_cb;
982 sys_data->add_cb = add_cb;
983 sys_data->remove_cb = remove_cb;
984}
985
986void ecs_set_system_udata(ecs_t* ecs, ecs_system_t sys, void* udata)
987{
988 ECS_ASSERT(ecs_is_not_null(ecs));
989 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
990 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
991
992 ecs->systems[sys.id].udata = udata;
993}
994
996{
997 ECS_ASSERT(ecs_is_not_null(ecs));
998 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
999 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1000
1001 return ecs->systems[sys.id].udata;
1002}
1003
1005{
1006 ECS_ASSERT(ecs_is_not_null(ecs));
1007 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1008 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1009
1010 ecs->systems[sys.id].mask = mask;
1011}
1012
1014{
1015 ECS_ASSERT(ecs_is_not_null(ecs));
1016 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1017 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1018
1019 return ecs->systems[sys.id].mask;
1020}
1021
1023{
1024 return ecs->systems[sys.id].entity_ids.size;
1025}
1026
1028{
1029 ECS_ASSERT(ecs_is_not_null(ecs));
1030
1031 ecs_id_t entity_id = 0;
1032
1033 // If there is an ID in the pool, pop it
1034 ecs_id_array_t* pool = &ecs->entity_pool;
1035
1036 if (0 != ecs_id_array_size(pool))
1037 {
1038 entity_id = ecs_id_array_pop(pool);
1039 }
1040 else
1041 {
1042 // Otherwise, issue a fresh ID
1043 entity_id = ecs->next_entity_id++;
1044
1045 // Grow the entities array if necessary
1046 if (entity_id >= ecs->entity_count)
1047 {
1048 size_t old_count = ecs->entity_count;
1049 size_t new_count = 2 * old_count;
1050
1051 ecs->entities = (ecs_entity_data_t*)ecs_realloc_zero(ecs, ecs->entities,
1052 old_count * sizeof(ecs_entity_data_t),
1053 new_count * sizeof(ecs_entity_data_t));
1054
1055 ecs->entity_count = new_count;
1056 }
1057 }
1058
1059 // Activate the entity and return a handle
1060 ecs->entities[entity_id].active = true;
1061 ecs->entities[entity_id].ready = true;
1062
1063 return ecs_make_entity(entity_id);
1064}
1065
1066bool ecs_is_ready(ecs_t* ecs, ecs_entity_t entity)
1067{
1068 ECS_ASSERT(ecs_is_not_null(ecs));
1069
1070 return ecs->entities[entity.id].ready;
1071}
1072
1073void ecs_destroy(ecs_t* ecs, ecs_entity_t entity)
1074{
1075 ECS_ASSERT(ecs_is_not_null(ecs));
1076 ECS_ASSERT(ecs_is_active(ecs, entity.id));
1077
1078 // Load entity data
1079 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1080
1081 // Remove entity from systems
1082 if (ecs_is_ready(ecs, entity))
1083 {
1084 ecs_remove_from_systems(ecs, entity);
1085 }
1086
1087 // Call destructors on entity components
1088 ecs_destruct(ecs, entity.id);
1089
1090 // Push entity ID into pool
1091 ecs_id_array_t* pool = &ecs->entity_pool;
1092 ecs_id_array_push(ecs, pool, entity.id);
1093
1094 // Reset entity (sets bitset to 0 and, active and ready to false)
1095 memset(entity_data, 0, sizeof(ecs_entity_data_t));
1096}
1097
1098bool ecs_has(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp)
1099{
1100 ECS_ASSERT(ecs_is_not_null(ecs));
1101 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1102 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1103
1104 // Load entity data
1105 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1106
1107 // Return true if the component belongs to the entity
1108 return ecs_bitset_test(&entity_data->comp_bits, comp.id);
1109}
1110
1111void* ecs_get(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp)
1112{
1113 ECS_ASSERT(ecs_is_not_null(ecs));
1114 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1115 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1116 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1117
1118 // Return pointer to component
1119 // eid0, eid1 eid2, ...
1120 // [comp0, comp1, comp2, ...]
1121 ecs_comp_array_t* comp_array = &ecs->comp_arrays[comp.id];
1122 return (char*)comp_array->data + (comp_array->size * entity.id);
1123}
1124
1125void* ecs_add(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp, void* args)
1126{
1127 ECS_ASSERT(ecs_is_not_null(ecs));
1128 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1129 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1130 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1131
1132 // Load entity data
1133 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1134
1135 // Load component
1136 ecs_comp_array_t* comp_array = &ecs->comp_arrays[comp.id];
1137 ecs_comp_data_t* comp_data = &ecs->comps[comp.id];
1138
1139 // Grow the component array
1140 ecs_comp_array_resize(ecs, comp_array, entity.id);
1141
1142 // Get pointer to component
1143 void* comp_ptr = ecs_get(ecs, entity, comp);
1144
1145 // Zero component
1146 memset(comp_ptr, 0, comp_array->size);
1147
1148 // Call constructor
1149 if (comp_data->constructor)
1150 comp_data->constructor(ecs, entity, comp_ptr, args);
1151
1152 // Set entity component bit that determines which systems this entity
1153 // belongs to
1154 ecs_bitset_flip(&entity_data->comp_bits, comp.id, true);
1155
1156 // Add or remove entity from systems
1157 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
1158 {
1159 ecs_sys_data_t* sys_data = &ecs->systems[sys_id];
1160
1161 if (ecs_entity_system_test(&sys_data->require_bits, &sys_data->exclude_bits, &entity_data->comp_bits))
1162 {
1163 if (ecs_sparse_set_add(ecs, &sys_data->entity_ids, entity.id))
1164 {
1165 if (sys_data->add_cb)
1166 sys_data->add_cb(ecs, entity, sys_data->udata);
1167 }
1168 }
1169 else // Just remove the entity if its components no longer match for whatever reason.
1170 {
1171 if (!ecs_bitset_is_zero(&sys_data->exclude_bits) &&
1172 ecs_sparse_set_remove(&sys_data->entity_ids, entity.id))
1173 {
1174 if (sys_data->remove_cb)
1175 sys_data->remove_cb(ecs, entity, sys_data->udata);
1176 }
1177 }
1178 }
1179
1180 // Return component
1181 return comp_ptr;
1182}
1183
1184void ecs_remove(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp)
1185{
1186 ECS_ASSERT(ecs_is_not_null(ecs));
1187 ECS_ASSERT(ecs_is_valid_component_id(comp.id));
1188 ECS_ASSERT(ecs_is_component_ready(ecs, comp.id));
1189 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1190
1191 // Load entity data
1192 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1193
1194 // Create bit mask with comp bit flipped on
1195 ecs_bitset_t comp_bit;
1196
1197 memset(&comp_bit, 0, sizeof(ecs_bitset_t));
1198 ecs_bitset_flip(&comp_bit, comp.id, true);
1199
1200 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
1201 {
1202 ecs_sys_data_t* sys_data = &ecs->systems[sys_id];
1203
1204 if (ecs_entity_system_test(&sys_data->require_bits, &sys_data->exclude_bits, &comp_bit))
1205 {
1206 if (ecs_sparse_set_remove(&sys_data->entity_ids, entity.id))
1207 {
1208 if (sys_data->remove_cb)
1209 sys_data->remove_cb(ecs, entity, sys_data->udata);
1210 }
1211 }
1212 else
1213 {
1214 if (!ecs_bitset_is_zero(&sys_data->exclude_bits) &&
1215 ecs_sparse_set_add(ecs, &sys_data->entity_ids, entity.id))
1216 {
1217 if (sys_data->add_cb)
1218 sys_data->add_cb(ecs, entity, sys_data->udata);
1219 }
1220 }
1221 }
1222
1223 ecs_comp_data_t* comp_data = &ecs->comps[comp.id];
1224
1225 if (comp_data->destructor)
1226 {
1227 void* comp_ptr = ecs_get(ecs, entity, comp);
1228 comp_data->destructor(ecs, entity, comp_ptr);
1229 }
1230
1231 // Reset the relevant component mask bit
1232 ecs_bitset_flip(&entity_data->comp_bits, comp.id, false);
1233}
1234
1235void ecs_queue_destroy(ecs_t* ecs, ecs_entity_t entity)
1236{
1237 ECS_ASSERT(ecs_is_not_null(ecs));
1238 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1239
1240 ecs_remove_from_systems(ecs, entity);
1241
1242 ecs->entities[entity.id].ready = false;
1243
1244 ecs_id_array_push(ecs, &ecs->destroy_queue, entity.id);
1245}
1246
1247void ecs_queue_remove(ecs_t* ecs, ecs_entity_t entity, ecs_comp_t comp)
1248{
1249 ECS_ASSERT(ecs_is_not_null(ecs));
1250 ECS_ASSERT(ecs_is_entity_ready(ecs, entity.id));
1251 ECS_ASSERT(ecs_has(ecs, entity, comp));
1252
1253 ecs_id_array_push(ecs, &ecs->remove_queue, entity.id);
1254 ecs_id_array_push(ecs, &ecs->remove_queue, comp.id);
1255}
1256
1258{
1259 ECS_ASSERT(ecs_is_not_null(ecs));
1260 ECS_ASSERT(ecs_is_valid_system_id(sys.id));
1261 ECS_ASSERT(ecs_is_system_ready(ecs, sys.id));
1262
1263 ecs_sys_data_t* sys_data = &ecs->systems[sys.id];
1264
1265 if (!sys_data->active)
1266 return 0;
1267
1268 if (0 != sys_data->mask && !(sys_data->mask & mask))
1269 return 0;
1270
1271 ecs_ret_t code = sys_data->system_cb(ecs,
1272 sys_data->entity_ids.dense,
1273 sys_data->entity_ids.size,
1274 sys_data->udata);
1275
1276 ecs_flush_destroyed(ecs);
1277 ecs_flush_removed(ecs);
1278
1279 return code;
1280}
1281
1283{
1284 ECS_ASSERT(ecs_is_not_null(ecs));
1285
1286 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
1287 {
1288 ecs_system_t sys = ecs_make_system(sys_id);
1289 ecs_ret_t code = ecs_run_system(ecs, sys, mask);
1290
1291 if (0 != code)
1292 return code;
1293 }
1294
1295 return 0;
1296}
1297
1298/*=============================================================================
1299 * Handle constructors
1300 *============================================================================*/
1301static inline ecs_entity_t ecs_make_entity(ecs_id_t id)
1302{
1303 ecs_entity_t entity = { id };
1304 return entity;
1305}
1306
1307static inline ecs_comp_t ecs_make_comp(ecs_id_t id)
1308{
1309 ecs_comp_t comp = { id };
1310 return comp;
1311}
1312
1313static inline ecs_system_t ecs_make_system(ecs_id_t id)
1314{
1315 ecs_system_t sys = { id };
1316 return sys;
1317}
1318
1319/*=============================================================================
1320 * Realloc wrapper
1321 *============================================================================*/
1322static void* ecs_realloc_zero(ecs_t* ecs, void* ptr, size_t old_size, size_t new_size)
1323{
1324 (void)ecs;
1325
1326 ptr = ECS_REALLOC(ptr, new_size, ecs->mem_ctx);
1327
1328 if (new_size > old_size && ptr) {
1329 size_t diff = new_size - old_size;
1330 void* start = ((char*)ptr)+ old_size;
1331 memset(start, 0, diff);
1332 }
1333
1334 return ptr;
1335}
1336
1337/*=============================================================================
1338 * Tests if entity is active (created)
1339 *============================================================================*/
1340static inline bool ecs_is_active(ecs_t* ecs, ecs_id_t entity_id)
1341{
1342 ECS_ASSERT(ecs_is_not_null(ecs));
1343 return ecs->entities[entity_id].active;
1344}
1345
1346/*=============================================================================
1347 * Calls destructors on all components of the entity
1348 *============================================================================*/
1349static void ecs_destruct(ecs_t* ecs, ecs_id_t entity_id)
1350{
1351 // Load entity
1352 ecs_entity_data_t* entity = &ecs->entities[entity_id];
1353
1354 // Loop through components and call the destructors
1355 for (ecs_id_t comp_id = 0; comp_id < ecs->comp_count; comp_id++)
1356 {
1357 if (ecs_bitset_test(&entity->comp_bits, comp_id))
1358 {
1359 ecs_comp_data_t* comp = &ecs->comps[comp_id];
1360
1361 if (comp->destructor)
1362 {
1363 // Get component pointer directly without ecs_get to avoid
1364 // ready assertion, since entity may be queued for destruction
1365 ecs_comp_array_t* comp_array = &ecs->comp_arrays[comp_id];
1366 void* comp_ptr = (char*)comp_array->data + (comp_array->size * entity_id);
1367 ecs_entity_t entity = ecs_make_entity(entity_id);
1368
1369 comp->destructor(ecs, entity, comp_ptr);
1370 }
1371 }
1372 }
1373}
1374
1375/*=============================================================================
1376 * Removes entity from ALL systems
1377 *============================================================================*/
1378static void ecs_remove_from_systems(ecs_t* ecs, ecs_entity_t entity)
1379{
1380 // Load entity
1381 ecs_entity_data_t* entity_data = &ecs->entities[entity.id];
1382
1383 for (ecs_id_t sys_id = 0; sys_id < ecs->system_count; sys_id++)
1384 {
1385 ecs_sys_data_t* sys_data = &ecs->systems[sys_id];
1386
1387 if (ecs_entity_system_test(&sys_data->require_bits,
1388 &sys_data->exclude_bits,
1389 &entity_data->comp_bits) &&
1390 ecs_sparse_set_remove(&sys_data->entity_ids, entity.id))
1391 {
1392 if (sys_data->remove_cb)
1393 sys_data->remove_cb(ecs, entity, sys_data->udata);
1394 }
1395 }
1396}
1397
1398/*=============================================================================
1399 * Functions to flush destroyed entity and removed component
1400 *============================================================================*/
1401
1402static void ecs_flush_destroyed(ecs_t* ecs)
1403{
1404 ecs_id_array_t* destroy_queue = &ecs->destroy_queue;
1405
1406 for (size_t i = 0; i < destroy_queue->size; i++)
1407 {
1408 ecs_id_t entity_id = destroy_queue->data[i];
1409
1410 if (ecs_is_active(ecs, entity_id))
1411 ecs_destroy(ecs, ecs_make_entity(entity_id));
1412 }
1413
1414 destroy_queue->size = 0;
1415}
1416
1417static void ecs_flush_removed(ecs_t* ecs)
1418{
1419 ecs_id_array_t* remove_queue = &ecs->remove_queue;
1420
1421 for (size_t i = 0; i < remove_queue->size; i += 2)
1422 {
1423 ecs_id_t entity_id = remove_queue->data[i];
1424
1425 if (ecs_is_active(ecs, entity_id))
1426 {
1427 ecs_id_t comp_id = remove_queue->data[i + 1];
1428 ecs_remove(ecs, ecs_make_entity(entity_id), ecs_make_comp(comp_id));
1429 }
1430 }
1431
1432 remove_queue->size = 0;
1433}
1434
1435
1436/*=============================================================================
1437 * Bitset functions
1438 *============================================================================*/
1439
1440#if ECS_MAX_COMPONENTS <= 64
1441
1442static inline bool ecs_bitset_is_zero(ecs_bitset_t* set)
1443{
1444 return *set == 0;
1445}
1446
1447static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on)
1448{
1449 if (on)
1450 *set |= ((uint64_t)1 << bit);
1451 else
1452 *set &= ~((uint64_t)1 << bit);
1453}
1454
1455static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit)
1456{
1457 return *set & ((uint64_t)1 << bit);
1458}
1459
1460static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1, ecs_bitset_t* set2)
1461{
1462 return *set1 & *set2;
1463}
1464
1465static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1, ecs_bitset_t* set2)
1466{
1467 return *set1 | *set2;
1468}
1469
1470static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set)
1471{
1472 return ~(*set);
1473}
1474
1475static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2)
1476{
1477 return *set1 == *set2;
1478}
1479
1480static inline bool ecs_bitset_true(ecs_bitset_t* set)
1481{
1482 return *set;
1483}
1484
1485#else // ECS_MAX_COMPONENTS
1486
1487static inline bool ecs_bitset_is_zero(ecs_bitset_t* set)
1488{
1489 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1490 {
1491 if (set->array[i] != 0)
1492 return false;
1493 }
1494
1495 return true;
1496}
1497
1498static inline void ecs_bitset_flip(ecs_bitset_t* set, int bit, bool on)
1499{
1500 int index = bit / ECS_BITSET_WIDTH;
1501
1502 if (on)
1503 set->array[index] |= ((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1504 else
1505 set->array[index] &= ~((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1506}
1507
1508static inline bool ecs_bitset_test(ecs_bitset_t* set, int bit)
1509{
1510 int index = bit / ECS_BITSET_WIDTH;
1511 return set->array[index] & ((uint64_t)1 << bit % ECS_BITSET_WIDTH);
1512}
1513
1514static inline ecs_bitset_t ecs_bitset_and(ecs_bitset_t* set1,
1515 ecs_bitset_t* set2)
1516{
1517 ecs_bitset_t set;
1518
1519 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1520 {
1521 set.array[i] = set1->array[i] & set2->array[i];
1522 }
1523
1524 return set;
1525}
1526
1527static inline ecs_bitset_t ecs_bitset_or(ecs_bitset_t* set1,
1528 ecs_bitset_t* set2)
1529{
1530 ecs_bitset_t set;
1531
1532 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1533 {
1534 set.array[i] = set1->array[i] | set2->array[i];
1535 }
1536
1537 return set;
1538}
1539
1540static inline ecs_bitset_t ecs_bitset_not(ecs_bitset_t* set)
1541{
1542 ecs_bitset_t out;
1543
1544 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1545 {
1546 out.array[i] = ~set->array[i];
1547 }
1548
1549 return out;
1550}
1551
1552static inline bool ecs_bitset_equal(ecs_bitset_t* set1, ecs_bitset_t* set2)
1553{
1554 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1555 {
1556 if (set1->array[i] != set2->array[i])
1557 {
1558 return false;
1559 }
1560 }
1561
1562 return true;
1563}
1564
1565static inline bool ecs_bitset_true(ecs_bitset_t* set)
1566{
1567 for (int i = 0; i < ECS_BITSET_SIZE; i++)
1568 {
1569 if (set->array[i])
1570 return true;
1571 }
1572
1573 return false;
1574}
1575
1576#endif // ECS_MAX_COMPONENTS
1577
1578/*=============================================================================
1579 * Sparse set functions
1580 *============================================================================*/
1581
1582static void ecs_sparse_set_init(ecs_t* ecs, ecs_sparse_set_t* set, size_t capacity)
1583{
1584 ECS_ASSERT(ecs_is_not_null(ecs));
1585 ECS_ASSERT(ecs_is_not_null(set));
1586 ECS_ASSERT(capacity > 0);
1587
1588 (void)ecs;
1589
1590 set->capacity = capacity;
1591 set->size = 0;
1592
1593 set->dense = (ecs_entity_t*)ECS_MALLOC(capacity * sizeof(ecs_id_t), ecs->mem_ctx);
1594 set->sparse = (size_t*) ECS_MALLOC(capacity * sizeof(size_t), ecs->mem_ctx);
1595
1596 memset(set->sparse, 0, capacity * sizeof(size_t));
1597}
1598
1599static void ecs_sparse_set_free(ecs_t* ecs, ecs_sparse_set_t* set)
1600{
1601 ECS_ASSERT(ecs_is_not_null(ecs));
1602 ECS_ASSERT(ecs_is_not_null(set));
1603
1604 (void)ecs;
1605
1606 ECS_FREE(set->dense, ecs->mem_ctx);
1607 ECS_FREE(set->sparse, ecs->mem_ctx);
1608}
1609
1610static bool ecs_sparse_set_add(ecs_t* ecs, ecs_sparse_set_t* set, ecs_id_t id)
1611{
1612 ECS_ASSERT(ecs_is_not_null(ecs));
1613 ECS_ASSERT(ecs_is_not_null(set));
1614
1615 (void)ecs;
1616
1617 // Grow sparse set if necessary
1618 if (id >= set->capacity)
1619 {
1620 size_t old_capacity = set->capacity;
1621 size_t new_capacity = old_capacity;
1622
1623 // Calculate new capacity
1624 new_capacity *= 2;
1625
1626 // Grow dense array
1627 set->dense = (ecs_entity_t*)ECS_REALLOC(set->dense,
1628 new_capacity * sizeof(ecs_id_t),
1629 ecs->mem_ctx);
1630
1631 // Grow sparse array and zero it
1632 set->sparse = (size_t*)ecs_realloc_zero(ecs,
1633 set->sparse,
1634 old_capacity * sizeof(size_t),
1635 new_capacity * sizeof(size_t));
1636
1637 // Set the new capacity
1638 set->capacity = new_capacity;
1639 }
1640
1641 // Check if ID exists within the set
1642 if (ecs_sparse_set_find(set, id, NULL))
1643 return false;
1644
1645 // Add ID to set
1646 set->dense[set->size].id = id;
1647 set->sparse[id] = set->size;
1648
1649 set->size++;
1650
1651 return true;
1652}
1653
1654static bool ecs_sparse_set_find(ecs_sparse_set_t* set, ecs_id_t id, size_t* found)
1655{
1656 ECS_ASSERT(ecs_is_not_null(set));
1657
1658 if (set->sparse[id] < set->size && set->dense[set->sparse[id]].id == id)
1659 {
1660 if (found) *found = set->sparse[id];
1661 return true;
1662 }
1663 else
1664 {
1665 if (found) *found = 0;
1666 return false;
1667 }
1668}
1669
1670static bool ecs_sparse_set_remove(ecs_sparse_set_t* set, ecs_id_t id)
1671{
1672 ECS_ASSERT(ecs_is_not_null(set));
1673
1674 if (!ecs_sparse_set_find(set, id, NULL))
1675 return false;
1676
1677 // Swap and remove (changes order of array)
1678 ecs_id_t tmp = set->dense[set->size - 1].id;
1679 set->dense[set->sparse[id]].id = tmp;
1680 set->sparse[tmp] = set->sparse[id];
1681
1682 set->size--;
1683
1684 return true;
1685}
1686
1687/*=============================================================================
1688 * System entity add/remove functions
1689 *============================================================================*/
1690
1691inline static bool ecs_entity_system_test(ecs_bitset_t* require_bits,
1692 ecs_bitset_t* exclude_bits,
1693 ecs_bitset_t* entity_bits)
1694{
1695 if (!ecs_bitset_is_zero(exclude_bits))
1696 {
1697 ecs_bitset_t overlap = ecs_bitset_and(entity_bits, exclude_bits);
1698
1699 if (ecs_bitset_true(&overlap))
1700 {
1701 return false;
1702 }
1703 }
1704
1705 ecs_bitset_t entity_and_require = ecs_bitset_and(entity_bits, require_bits);
1706 return ecs_bitset_equal(&entity_and_require, require_bits);
1707}
1708
1709/*=============================================================================
1710 * ID array functions
1711 *============================================================================*/
1712
1713inline static void ecs_id_array_init(ecs_t* ecs, ecs_id_array_t* array, int capacity)
1714{
1715 ECS_ASSERT(ecs_is_not_null(ecs));
1716 ECS_ASSERT(ecs_is_not_null(array));
1717 ECS_ASSERT(capacity > 0);
1718
1719 (void)ecs;
1720
1721 array->size = 0;
1722 array->capacity = capacity;
1723 array->data = (ecs_id_t*)ECS_MALLOC(capacity * sizeof(ecs_id_t), ecs->mem_ctx);
1724}
1725
1726inline static void ecs_id_array_free(ecs_t* ecs, ecs_id_array_t* array)
1727{
1728 ECS_ASSERT(ecs_is_not_null(ecs));
1729 ECS_ASSERT(ecs_is_not_null(array));
1730
1731 (void)ecs;
1732
1733 ECS_FREE(array->data, ecs->mem_ctx);
1734}
1735
1736inline static void ecs_id_array_push(ecs_t* ecs, ecs_id_array_t* array, ecs_id_t id)
1737{
1738 ECS_ASSERT(ecs_is_not_null(ecs));
1739 ECS_ASSERT(ecs_is_not_null(array));
1740 ECS_ASSERT(array->capacity > 0);
1741
1742 (void)ecs;
1743
1744 if (array->size == array->capacity)
1745 {
1746 array->capacity *= 2;
1747 array->data = (ecs_id_t*)ECS_REALLOC(array->data,
1748 array->capacity * sizeof(ecs_id_t),
1749 ecs->mem_ctx);
1750 }
1751
1752 array->data[array->size++] = id;
1753}
1754
1755inline static ecs_id_t ecs_id_array_pop(ecs_id_array_t* array)
1756{
1757 ECS_ASSERT(ecs_is_not_null(array));
1758 return array->data[--array->size];
1759}
1760
1761inline static int ecs_id_array_size(ecs_id_array_t* array)
1762{
1763 return array->size;
1764}
1765
1766static void ecs_comp_array_init(ecs_t* ecs, ecs_comp_array_t* array, size_t size, size_t capacity)
1767{
1768 ECS_ASSERT(ecs_is_not_null(ecs));
1769 ECS_ASSERT(ecs_is_not_null(array));
1770
1771 (void)ecs;
1772
1773 memset(array, 0, sizeof(ecs_comp_array_t));
1774
1775 array->capacity = capacity;
1776 array->size = size;
1777 array->data = ECS_MALLOC(size * capacity, ecs->mem_ctx);
1778}
1779
1780static void ecs_comp_array_free(ecs_t* ecs, ecs_comp_array_t* array)
1781{
1782 ECS_ASSERT(ecs_is_not_null(ecs));
1783 ECS_ASSERT(ecs_is_not_null(array));
1784
1785 (void)ecs;
1786
1787 ECS_FREE(array->data, ecs->mem_ctx);
1788}
1789
1790static void ecs_comp_array_resize(ecs_t* ecs, ecs_comp_array_t* array, size_t capacity)
1791{
1792 ECS_ASSERT(ecs_is_not_null(ecs));
1793 ECS_ASSERT(ecs_is_not_null(array));
1794
1795 (void)ecs;
1796
1797 if (capacity >= array->capacity)
1798 {
1799 array->capacity *= 2;
1800 array->data = ECS_REALLOC(array->data,
1801 array->capacity * array->size,
1802 ecs->mem_ctx);
1803 }
1804}
1805
1806/*=============================================================================
1807 * Validation functions
1808 *============================================================================*/
1809#ifndef NDEBUG
1810static bool ecs_is_not_null(void* ptr)
1811{
1812 return NULL != ptr;
1813}
1814
1815static bool ecs_is_valid_component_id(ecs_id_t id)
1816{
1817 return id < ECS_MAX_COMPONENTS;
1818}
1819
1820static bool ecs_is_valid_system_id(ecs_id_t id)
1821{
1822 return id < ECS_MAX_SYSTEMS;
1823}
1824
1825static bool ecs_is_entity_ready(ecs_t* ecs, ecs_id_t entity_id)
1826{
1827 return ecs->entities[entity_id].ready;
1828}
1829
1830static bool ecs_is_component_ready(ecs_t* ecs, ecs_id_t comp_id)
1831{
1832 return comp_id < ecs->comp_count;
1833}
1834
1835static bool ecs_is_system_ready(ecs_t* ecs, ecs_id_t sys_id)
1836{
1837 return sys_id < ecs->system_count;
1838}
1839
1840#endif // NDEBUG
1841
1842#endif // PICO_ECS_IMPLEMENTATION
1843
1844/*
1845 ----------------------------------------------------------------------------
1846 This software is available under two licenses (A) or (B). You may choose
1847 either one as you wish:
1848 ----------------------------------------------------------------------------
1849
1850 (A) The zlib License
1851
1852 Copyright (c) 2025 James McLean
1853
1854 This software is provided 'as-is', without any express or implied warranty.
1855 In no event will the authors be held liable for any damages arising from the
1856 use of this software.
1857
1858 Permission is granted to anyone to use this software for any purpose,
1859 including commercial applications, and to alter it and redistribute it
1860 freely, subject to the following restrictions:
1861
1862 1. The origin of this software must not be misrepresented; you must not
1863 claim that you wrote the original software. If you use this software in a
1864 product, an acknowledgment in the product documentation would be appreciated
1865 but is not required.
1866
1867 2. Altered source versions must be plainly marked as such, and must not be
1868 misrepresented as being the original software.
1869
1870 3. This notice may not be removed or altered from any source distribution.
1871
1872 ----------------------------------------------------------------------------
1873
1874 (B) Public Domain (www.unlicense.org)
1875
1876 This is free and unencumbered software released into the public domain.
1877
1878 Anyone is free to copy, modify, publish, use, compile, sell, or distribute
1879 this software, either in source code form or as a compiled binary, for any
1880 purpose, commercial or non-commercial, and by any means.
1881
1882 In jurisdictions that recognize copyright laws, the author or authors of
1883 this software dedicate any and all copyright interest in the software to the
1884 public domain. We make this dedication for the benefit of the public at
1885 large and to the detriment of our heirs and successors. We intend this
1886 dedication to be an overt act of relinquishment in perpetuity of all present
1887 and future rights to this software under copyright law.
1888
1889 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1890 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1891 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1892 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1893 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1894 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1895*/
1896
1897// EoF
void(* ecs_destructor_fn)(ecs_t *ecs, ecs_entity_t entity, void *comp_ptr)
Called when a component is destroyed (via ecs_remove or ecs_destroy)
Definition pico_ecs.h:256
void ecs_enable_system(ecs_t *ecs, ecs_system_t sys)
Enables a system.
void ecs_queue_remove(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp)
Queues a component for removal from the specified entity.
void * ecs_add(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp, void *args)
Adds a component instance to an entity.
#define ECS_MASK_TYPE
Determine mask type.
Definition pico_ecs.h:177
ecs_system_t ecs_define_system(ecs_t *ecs, ecs_mask_t mask, ecs_system_fn system_cb, ecs_added_fn add_cb, ecs_removed_fn remove_cb, void *udata)
Defines a system.
ecs_entity_t ecs_invalid_entity()
Returns an invalid entity.
void * ecs_get(ecs_t *ecs, ecs_entity_t entity, ecs_comp_t comp)
Gets a component instance associated with an entity.
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:183
void ecs_queue_destroy(ecs_t *ecs, ecs_entity_t entity)
Queues an entity for destruction after the current system returns.
void ecs_exclude_component(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_set_system_callbacks(ecs_t *ecs, ecs_system_t sys, ecs_system_fn system_cb, ecs_added_fn add_cb, ecs_removed_fn remove_cb)
Updates the callbacks for an existing system.
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:171
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:289
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, ecs_constructor_fn constructor, ecs_destructor_fn destructor)
Defines a component.
void ecs_free(ecs_t *ecs)
Destroys an ECS context.
void(* ecs_removed_fn)(ecs_t *ecs, ecs_entity_t entity, void *udata)
Called when an entity is removed from a system.
Definition pico_ecs.h:310
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:159
void ecs_reset(ecs_t *ecs)
Removes all entities from the ECS, preserving systems and components.
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.
size_t ecs_get_system_entity_count(ecs_t *ecs, ecs_system_t sys)
Returns the number of entities assigned to the specified system.
void ecs_require_component(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_ret_t ecs_run_system(ecs_t *ecs, ecs_system_t sys, ecs_mask_t mask)
Update an individual system.
void(* ecs_added_fn)(ecs_t *ecs, ecs_entity_t entity, void *udata)
Called when an entity is added to a system.
Definition pico_ecs.h:301
int32_t ecs_ret_t
Return code for system callback and calling functions.
Definition pico_ecs.h:188
void ecs_set_system_mask(ecs_t *ecs, ecs_system_t sys, ecs_mask_t mask)
Sets the system's mask.
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.
void(* ecs_constructor_fn)(ecs_t *ecs, ecs_entity_t entity, void *comp_ptr, void *args)
Called when a component is created (via ecs_add)
Definition pico_ecs.h:244
ecs_entity_t ecs_create(ecs_t *ecs)
Creates an entity.
ecs_t * ecs_new(size_t entity_count, void *mem_ctx)
Creates an ECS context.
bool ecs_is_invalid_entity(ecs_entity_t entity)
Returns true if the entity is invalid and false otherwise.
#define ECS_ID_TYPE
Determine ID type.
Definition pico_ecs.h:165
ecs_mask_t ecs_get_system_mask(ecs_t *ecs, ecs_system_t sys)
Returns the system mask.
A component handle.
Definition pico_ecs.h:198
ecs_id_t id
Definition pico_ecs.h:198
An entity handle.
Definition pico_ecs.h:193
ecs_id_t id
Definition pico_ecs.h:193
A system handle.
Definition pico_ecs.h:203
ecs_id_t id
Definition pico_ecs.h:203