#define flecs_STATIC #ifndef FLECS_H #define FLECS_H /* FLECS_LEGACY should be defined when building for C89 */ // #define FLECS_LEGACY /* FLECS_NO_CPP should be defined when building for C++ without the C++ API */ // #define FLECS_NO_CPP /* FLECS_CUSTOM_BUILD should be defined when manually selecting features */ // #define FLECS_CUSTOM_BUILD #ifndef FLECS_CUSTOM_BUILD /* Modules */ #define FLECS_SYSTEM #define FLECS_PIPELINE #define FLECS_TIMER /* Addons */ #define FLECS_BULK #define FLECS_DBG #define FLECS_MODULE #define FLECS_QUEUE #define FLECS_READER_WRITER #define FLECS_SNAPSHOT #define FLECS_DIRECT_ACCESS #define FLECS_STATS #endif /* Set to double or int to increase accuracy of time keeping. Note that when * using an integer type, an application has to provide the delta_time values * to the progress() function, as the code that measures time requires a * floating point type. */ #ifndef FLECS_FLOAT #define FLECS_FLOAT float #endif /** * @file api_defines.h * @brief Supporting defines for the public API. * * This file contains constants / macro's that are typically not used by an * application but support the public API, and therefore must be exposed. This * header should not be included by itself. */ #ifndef FLECS_API_DEFINES_H #define FLECS_API_DEFINES_H /* Standard library dependencies */ #include #include #include #include #include #include #include #include /* Non-standard but required. If not provided by platform, add manually. */ #include /* Contains macro's for importing / exporting symbols */ /* ) (.) .|. | | _.--| |--._ .-'; ;`-'& ; `&. \ & ; & &_/ |"""---...---"""| \ | | | | | | | / `---.|.|.|.---' * This file is generated by bake.lang.c for your convenience. Headers of * dependencies will automatically show up in this file. Include bake_config.h * in your main project file. Do not edit! */ #ifndef FLECS_BAKE_CONFIG_H #define FLECS_BAKE_CONFIG_H /* Headers of public dependencies */ /* No dependencies */ /* Convenience macro for exporting symbols */ #ifndef flecs_STATIC #if flecs_EXPORTS && (defined(_MSC_VER) || defined(__MINGW32__)) #define FLECS_API __declspec(dllexport) #elif flecs_EXPORTS #define FLECS_API __attribute__((__visibility__("default"))) #elif defined _MSC_VER #define FLECS_API __declspec(dllimport) #else #define FLECS_API #endif #else #define FLECS_API #endif #endif #ifdef __cplusplus extern "C" { #endif #ifdef __BAKE_LEGACY__ #define FLECS_LEGACY #endif //////////////////////////////////////////////////////////////////////////////// //// Language support defines //////////////////////////////////////////////////////////////////////////////// #ifndef FLECS_LEGACY #include #endif /* The API uses the native bool type in C++, or a custom one in C */ #if !defined(__cplusplus) && !defined(__bool_true_false_are_defined) #undef bool #undef true #undef false typedef char bool; #define false 0 #define true !false #endif typedef uint32_t ecs_flags32_t; typedef uint64_t ecs_flags64_t; /* Keep unsigned integers out of the codebase as they do more harm than good */ typedef int32_t ecs_size_t; #define ECS_SIZEOF(T) (ecs_size_t)sizeof(T) /* Use alignof in C++, or a trick in C. */ #ifdef __cplusplus #define ECS_ALIGNOF(T) (int64_t)alignof(T) #elif defined(_MSC_VER) #define ECS_ALIGNOF(T) (int64_t)__alignof(T) #elif defined(__GNUC__) #define ECS_ALIGNOF(T) (int64_t)__alignof__(T) #else #define ECS_ALIGNOF(T) ((int64_t)&((struct { char c; T d; } *)0)->d) #endif #if defined(__GNUC__) #define ECS_UNUSED __attribute__((unused)) #else #define ECS_UNUSED #endif #define ECS_ALIGN(size, alignment) (ecs_size_t)((((((size_t)size) - 1) / ((size_t)alignment)) + 1) * ((size_t)alignment)) /* Simple utility for determining the max of two values */ #define ECS_MAX(a, b) ((a > b) ? a : b) //////////////////////////////////////////////////////////////////////////////// //// Reserved component ids //////////////////////////////////////////////////////////////////////////////// /** Builtin component ids */ #define FLECS__EEcsComponent (1) #define FLECS__EEcsComponentLifecycle (2) #define FLECS__EEcsType (3) #define FLECS__EEcsName (6) /** System module component ids */ #define FLECS__EEcsTrigger (4) #define FLECS__EEcsSystem (5) #define FLECS__EEcsTickSource (7) #define FLECS__EEcsSignatureExpr (8) #define FLECS__EEcsSignature (9) #define FLECS__EEcsQuery (10) #define FLECS__EEcsIterAction (11) #define FLECS__EEcsContext (12) /** Pipeline module component ids */ #define FLECS__EEcsPipelineQuery (13) /** Timer module component ids */ #define FLECS__EEcsTimer (14) #define FLECS__EEcsRateFilter (15) //////////////////////////////////////////////////////////////////////////////// //// Entity id macro's //////////////////////////////////////////////////////////////////////////////// #define ECS_ROLE_MASK ((ecs_entity_t)0xFF << 56) #define ECS_ENTITY_MASK ((uint64_t)0xFFFFFFFF) #define ECS_GENERATION_MASK ((uint64_t)0xFFFF << 32) #define ECS_GENERATION(e) ((e & ECS_GENERATION_MASK) >> 32) #define ECS_GENERATION_INC(e) ((e & ~ECS_GENERATION_MASK) | ((ECS_GENERATION(e) + 1) << 32)) #define ECS_COMPONENT_MASK ((ecs_entity_t)~ECS_ROLE_MASK) #define ECS_TYPE_ROLE_START ECS_CHILDOF #define ECS_HAS_ROLE(e, role) ((e & ECS_ROLE_MASK) == ECS_##role) //////////////////////////////////////////////////////////////////////////////// //// Convert between C typenames and variables //////////////////////////////////////////////////////////////////////////////// /** Translate C type to ecs_type_t variable. */ #define ecs_type(T) FLECS__T##T /** Translate C type to entity id. */ #define ecs_typeid(T) FLECS__E##T /* DEPRECATED: old way to get entity id from type */ #define ecs_entity(T) ecs_typeid(T) /** Translate C type to module struct. */ #define ecs_module(T) FLECS__M##T /** Translate C type to module struct. */ #define ecs_module_ptr(T) FLECS__M##T##_ptr /** Translate C type to module struct. */ #define ecs_iter_action(T) FLECS__F##T #ifndef FLECS_LEGACY //////////////////////////////////////////////////////////////////////////////// //// Utilities for working with trait identifiers //////////////////////////////////////////////////////////////////////////////// #define ecs_entity_t_lo(value) ((uint32_t)(value)) #define ecs_entity_t_hi(value) ((uint32_t)((value) >> 32)) #define ecs_entity_t_comb(v1, v2) (((uint64_t)(v2) << 32) + (uint32_t)(v1)) #define ecs_trait(comp, trait) ECS_TRAIT | ecs_entity_t_comb(comp, trait) //////////////////////////////////////////////////////////////////////////////// //// Convenience macro's for ctor, dtor, move and copy //////////////////////////////////////////////////////////////////////////////// /* Constructor / destructor convenience macro */ #define ECS_XTOR_IMPL(type, postfix, var, ...)\ void type##_##postfix(\ ecs_world_t *world,\ ecs_entity_t component,\ const ecs_entity_t *entity_ptr,\ void *_ptr,\ size_t _size,\ int32_t _count,\ void *ctx)\ {\ (void)world;\ (void)component;\ (void)entity_ptr;\ (void)_ptr;\ (void)_size;\ (void)_count;\ (void)ctx;\ for (int32_t i = 0; i < _count; i ++) {\ ecs_entity_t entity = entity_ptr[i];\ type *var = &((type*)_ptr)[i];\ (void)entity;\ (void)var;\ __VA_ARGS__\ }\ } /* Copy convenience macro */ #define ECS_COPY_IMPL(type, dst_var, src_var, ...)\ void type##_##copy(\ ecs_world_t *world,\ ecs_entity_t component,\ const ecs_entity_t *dst_entities,\ const ecs_entity_t *src_entities,\ void *_dst_ptr,\ const void *_src_ptr,\ size_t _size,\ int32_t _count,\ void *ctx)\ {\ (void)world;\ (void)component;\ (void)dst_entities;\ (void)src_entities;\ (void)_dst_ptr;\ (void)_src_ptr;\ (void)_size;\ (void)_count;\ (void)ctx;\ for (int32_t i = 0; i < _count; i ++) {\ ecs_entity_t dst_entity = dst_entities[i];\ ecs_entity_t src_entity = src_entities[i];\ type *dst_var = &((type*)_dst_ptr)[i];\ type *src_var = &((type*)_src_ptr)[i];\ (void)dst_entity;\ (void)src_entity;\ (void)dst_var;\ (void)src_var;\ __VA_ARGS__\ }\ } /* Move convenience macro */ #define ECS_MOVE_IMPL(type, dst_var, src_var, ...)\ void type##_##move(\ ecs_world_t *world,\ ecs_entity_t component,\ const ecs_entity_t *dst_entities,\ const ecs_entity_t *src_entities,\ void *_dst_ptr,\ void *_src_ptr,\ size_t _size,\ int32_t _count,\ void *ctx)\ {\ (void)world;\ (void)component;\ (void)dst_entities;\ (void)src_entities;\ (void)_dst_ptr;\ (void)_src_ptr;\ (void)_size;\ (void)_count;\ (void)ctx;\ for (int32_t i = 0; i < _count; i ++) {\ ecs_entity_t dst_entity = dst_entities[i];\ ecs_entity_t src_entity = src_entities[i];\ type *dst_var = &((type*)_dst_ptr)[i];\ type *src_var = &((type*)_src_ptr)[i];\ (void)dst_entity;\ (void)src_entity;\ (void)dst_var;\ (void)src_var;\ __VA_ARGS__\ }\ } #endif #ifdef __cplusplus } #endif #endif /* This is an implementation of a simple vector type. The vector is allocated in * a single block of memory, with the element count, and allocated number of * elements encoded in the block. As this vector is used for user-types it has * been designed to support alignments higher than 8 bytes. This makes the size * of the vector header variable in size. To reduce the overhead associated with * retrieving or computing this size, the functions are wrapped in macro calls * that compute the header size at compile time. * * The API provides a number of _t macro's, which accept a size and alignment. * These macro's are used when no compile-time type is available. * * The vector guarantees contiguous access to its elements. When an element is * removed from the vector, the last element is copied to the removed element. * * The API requires passing in the type of the vector. This type is used to test * whether the size of the provided type equals the size of the type with which * the vector was created. In release mode this check is not performed. * * When elements are added to the vector, it will automatically resize to the * next power of two. This can change the pointer of the vector, which is why * operations that can increase the vector size, accept a double pointer to the * vector. */ #ifndef FLECS_VECTOR_H #define FLECS_VECTOR_H #ifdef __cplusplus extern "C" { #endif /* Public, so we can do compile-time header size calculation */ struct ecs_vector_t { int32_t count; int32_t size; #ifndef NDEBUG int64_t elem_size; #endif }; /* Compute the header size of the vector from size & alignment */ #define ECS_VECTOR_U(size, alignment) size, ECS_MAX(ECS_SIZEOF(ecs_vector_t), alignment) /* Compute the header size of the vector from a provided compile-time type */ #define ECS_VECTOR_T(T) ECS_VECTOR_U(ECS_SIZEOF(T), ECS_ALIGNOF(T)) /* Utility macro's for creating vector on stack */ #ifndef NDEBUG #define ECS_VECTOR_VALUE(T, elem_count)\ {\ .elem_size = (int32_t)(ECS_SIZEOF(T)),\ .count = elem_count,\ .size = elem_count\ } #else #define ECS_VECTOR_VALUE(T, elem_count)\ {\ .count = elem_count,\ .size = elem_count\ } #endif #define ECS_VECTOR_DECL(name, T, elem_count)\ struct {\ union {\ ecs_vector_t vector;\ uint64_t align;\ } header;\ T array[elem_count];\ } __##name##_value = {\ .header.vector = ECS_VECTOR_VALUE(T, elem_count)\ };\ const ecs_vector_t *name = (ecs_vector_t*)&__##name##_value #define ECS_VECTOR_IMPL(name, T, elems, elem_count)\ ecs_os_memcpy(__##name##_value.array, elems, sizeof(T) * elem_count) #define ECS_VECTOR_STACK(name, T, elems, elem_count)\ ECS_VECTOR_DECL(name, T, elem_count);\ ECS_VECTOR_IMPL(name, T, elems, elem_count) typedef struct ecs_vector_t ecs_vector_t; typedef int (*ecs_comparator_t)( const void* p1, const void *p2); /** Create new vector. */ FLECS_API ecs_vector_t* _ecs_vector_new( ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_vector_new(T, elem_count) \ _ecs_vector_new(ECS_VECTOR_T(T), elem_count) #define ecs_vector_new_t(size, alignment, elem_count) \ _ecs_vector_new(ECS_VECTOR_U(size, alignment), elem_count) /* Create new vector, initialize it with provided array */ FLECS_API ecs_vector_t* _ecs_vector_from_array( ecs_size_t elem_size, int16_t offset, int32_t elem_count, void *array); #define ecs_vector_from_array(T, elem_count, array)\ _ecs_vector_from_array(ECS_VECTOR_T(T), elem_count, array) /* Initialize vector with zero's */ FLECS_API void _ecs_vector_zero( ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset); #define ecs_vector_zero(vector, T) \ _ecs_vector_zero(vector, ECS_VECTOR_T(T)) /** Free vector */ FLECS_API void ecs_vector_free( ecs_vector_t *vector); /** Clear values in vector */ FLECS_API void ecs_vector_clear( ecs_vector_t *vector); /** Assert when the provided size does not match the vector type. */ FLECS_API void ecs_vector_assert_size( ecs_vector_t* vector_inout, ecs_size_t elem_size); /** Assert when the provided alignment does not match the vector type. */ FLECS_API void ecs_vector_assert_alignment( ecs_vector_t* vector, ecs_size_t elem_alignment); /** Add element to vector. */ FLECS_API void* _ecs_vector_add( ecs_vector_t **array_inout, ecs_size_t elem_size, int16_t offset); #define ecs_vector_add(vector, T) \ ((T*)_ecs_vector_add(vector, ECS_VECTOR_T(T))) #define ecs_vector_add_t(vector, size, alignment) \ _ecs_vector_add(vector, ECS_VECTOR_U(size, alignment)) /** Add n elements to the vector. */ FLECS_API void* _ecs_vector_addn( ecs_vector_t **array_inout, ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_vector_addn(vector, T, elem_count) \ ((T*)_ecs_vector_addn(vector, ECS_VECTOR_T(T), elem_count)) #define ecs_vector_addn_t(vector, size, alignment, elem_count) \ _ecs_vector_addn(vector, ECS_VECTOR_U(size, alignment), elem_count) /** Get element from vector. */ FLECS_API void* _ecs_vector_get( const ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset, int32_t index); #define ecs_vector_get(vector, T, index) \ ((T*)_ecs_vector_get(vector, ECS_VECTOR_T(T), index)) #define ecs_vector_get_t(vector, size, alignment, index) \ _ecs_vector_get(vector, ECS_VECTOR_U(size, alignment), index) /** Get last element from vector. */ FLECS_API void* _ecs_vector_last( const ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset); #define ecs_vector_last(vector, T) \ (T*)_ecs_vector_last(vector, ECS_VECTOR_T(T)) /** Set minimum size for vector. If the current size of the vector is larger, * the function will have no side effects. */ FLECS_API int32_t _ecs_vector_set_min_size( ecs_vector_t **array_inout, ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_vector_set_min_size(vector, T, size) \ _ecs_vector_set_min_size(vector, ECS_VECTOR_T(T), size) /** Set minimum count for vector. If the current count of the vector is larger, * the function will have no side effects. */ FLECS_API int32_t _ecs_vector_set_min_count( ecs_vector_t **vector_inout, ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_vector_set_min_count(vector, T, size) \ _ecs_vector_set_min_count(vector, ECS_VECTOR_T(T), size) /** Remove last element. This operation requires no swapping of values. */ FLECS_API void ecs_vector_remove_last( ecs_vector_t *vector); /** Remove last value, store last element in provided value. */ FLECS_API bool _ecs_vector_pop( ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset, void *value); #define ecs_vector_pop(vector, T, value) \ _ecs_vector_pop(vector, ECS_VECTOR_T(T), value) /** Append element at specified index to another vector. */ FLECS_API int32_t _ecs_vector_move_index( ecs_vector_t **dst, ecs_vector_t *src, ecs_size_t elem_size, int16_t offset, int32_t index); #define ecs_vector_move_index(dst, src, T, index) \ _ecs_vector_move_index(dst, src, ECS_VECTOR_T(T), index) /** Remove element at specified index. Moves the last value to the index. */ FLECS_API int32_t _ecs_vector_remove_index( ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset, int32_t index); #define ecs_vector_remove_index(vector, T, index) \ _ecs_vector_remove_index(vector, ECS_VECTOR_T(T), index) #define ecs_vector_remove_index_t(vector, size, alignment, index) \ _ecs_vector_remove_index(vector, ECS_VECTOR_U(size, alignment), index) /** Shrink vector to make the size match the count. */ FLECS_API void _ecs_vector_reclaim( ecs_vector_t **vector, ecs_size_t elem_size, int16_t offset); #define ecs_vector_reclaim(vector, T)\ _ecs_vector_reclaim(vector, ECS_VECTOR_T(T)) /** Grow size of vector with provided number of elements. */ FLECS_API int32_t _ecs_vector_grow( ecs_vector_t **vector, ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_vector_grow(vector, T, size) \ _ecs_vector_grow(vector, ECS_VECTOR_T(T), size) /** Set allocation size of vector. */ FLECS_API int32_t _ecs_vector_set_size( ecs_vector_t **vector, ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_vector_set_size(vector, T, elem_count) \ _ecs_vector_set_size(vector, ECS_VECTOR_T(T), elem_count) #define ecs_vector_set_size_t(vector, size, alignment, elem_count) \ _ecs_vector_set_size(vector, ECS_VECTOR_U(size, alignment), elem_count) /** Set count of vector. If the size of the vector is smaller than the provided * count, the vector is resized. */ FLECS_API int32_t _ecs_vector_set_count( ecs_vector_t **vector, ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_vector_set_count(vector, T, elem_count) \ _ecs_vector_set_count(vector, ECS_VECTOR_T(T), elem_count) #define ecs_vector_set_count_t(vector, size, alignment, elem_count) \ _ecs_vector_set_count(vector, ECS_VECTOR_U(size, alignment), elem_count) /** Return number of elements in vector. */ FLECS_API int32_t ecs_vector_count( const ecs_vector_t *vector); /** Return size of vector. */ FLECS_API int32_t ecs_vector_size( const ecs_vector_t *vector); /** Return first element of vector. */ FLECS_API void* _ecs_vector_first( const ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset); #define ecs_vector_first(vector, T) \ ((T*)_ecs_vector_first(vector, ECS_VECTOR_T(T))) #define ecs_vector_first_t(vector, size, alignment) \ _ecs_vector_first(vector, ECS_VECTOR_U(size, alignment)) /** Sort elements in vector. */ FLECS_API void _ecs_vector_sort( ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset, ecs_comparator_t compare_action); #define ecs_vector_sort(vector, T, compare_action) \ _ecs_vector_sort(vector, ECS_VECTOR_T(T), compare_action) /** Return memory occupied by vector. */ FLECS_API void _ecs_vector_memory( const ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset, int32_t *allocd, int32_t *used); #define ecs_vector_memory(vector, T, allocd, used) \ _ecs_vector_memory(vector, ECS_VECTOR_T(T), allocd, used) #define ecs_vector_memory_t(vector, size, alignment, allocd, used) \ _ecs_vector_memory(vector, ECS_VECTOR_U(size, alignment), allocd, used) /** Copy vectors */ FLECS_API ecs_vector_t* _ecs_vector_copy( const ecs_vector_t *src, ecs_size_t elem_size, int16_t offset); #define ecs_vector_copy(src, T) \ _ecs_vector_copy(src, ECS_VECTOR_T(T)) #define ecs_vector_copy_t(src, size, alignment) \ _ecs_vector_copy(src, ECS_VECTOR_U(size, alignment)) #ifndef FLECS_LEGACY #define ecs_vector_each(vector, T, var, ...)\ {\ int var##_i, var##_count = ecs_vector_count(vector);\ T* var##_array = ecs_vector_first(vector, T);\ for (var##_i = 0; var##_i < var##_count; var##_i ++) {\ T* var = &var##_array[var##_i];\ __VA_ARGS__\ }\ } #endif #ifdef __cplusplus } #endif /** C++ wrapper for vector class. */ #ifdef __cplusplus #ifndef FLECS_NO_CPP #include namespace flecs { template class vector_iterator { public: explicit vector_iterator(T* value, int index) { m_value = value; m_index = index; } bool operator!=(vector_iterator const& other) const { return m_index != other.m_index; } T const& operator*() const { return m_value[m_index]; } vector_iterator& operator++() { ++m_index; return *this; } private: T* m_value; int m_index; }; template class vector { public: explicit vector(ecs_vector_t *v) : m_vector( v ) { } vector(int32_t count = 0) : m_vector( nullptr ) { if (count) { init(count); } } vector(std::initializer_list elems) : m_vector( nullptr) { init(elems.size()); *this = elems; } void operator=(std::initializer_list elems) { for (auto elem : elems) { this->add(elem); } } T& operator[](size_t index) { return ecs_vector_get(m_vector, T, index)[0]; } vector_iterator begin() { return vector_iterator( ecs_vector_first(m_vector, T), 0); } vector_iterator end() { return vector_iterator( ecs_vector_last(m_vector, T), ecs_vector_count(m_vector)); } void clear() { ecs_vector_clear(m_vector); } void add(T& value) { T* elem = ecs_vector_add(&m_vector, T); *elem = value; } void add(T&& value) { T* elem = ecs_vector_add(&m_vector, T); *elem = value; } T& get(int32_t index) { return ecs_vector_get(m_vector, T, index); } T& first() { return ecs_vector_first(m_vector, T); } T& last() { return ecs_vector_last(m_vector, T); } int32_t count() { return ecs_vector_count(m_vector); } int32_t size() { return ecs_vector_size(m_vector); } ecs_vector_t *ptr() { return m_vector; } void ptr(ecs_vector_t *ptr) { m_vector = ptr; } private: void init(int32_t count) { m_vector = ecs_vector_new(T, count); } ecs_vector_t *m_vector; }; } #endif #endif #endif /** This is an implementation of a paged sparse set that stores the payload in * the sparse array. * * A sparse set has a dense and a sparse array. The sparse array is directly * indexed by a 64 bit identifier. The sparse element is linked with a dense * element, which allows for liveliness checking. The liveliness check itself * can be performed by doing (psuedo code): * dense[sparse[sparse_id].dense] == sparse_id * * To ensure that the sparse array doesn't have to grow to a large size when * using large sparse_id's, the sparse set uses paging. This cuts up the array * into several pages of 4096 elements. When an element is set, the sparse set * ensures that the corresponding page is created. The page associated with an * id is determined by shifting a bit 12 bits to the right. * * The sparse set keeps track of a generation count per id, which is increased * each time an id is deleted. The generation is encoded in the returned id. * * This sparse set implementation stores payload in the sparse array, which is * not typical. The reason for this is to guarantee that (in combination with * paging) the returned payload pointers are stable. This allows for various * optimizations in the parts of the framework that uses the sparse set. * * The sparse set has been designed so that new ids can be generated in bulk, in * an O(1) operation. The way this works is that once a dense-sparse pair is * created, it is never unpaired. Instead it is moved to the end of the dense * array, and the sparse set stores an additional count to keep track of the * last alive id in the sparse set. To generate new ids in bulk, the sparse set * only needs to increase this count by the number of requested ids. */ #ifndef FLECS_SPARSE_H #define FLECS_SPARSE_H #ifdef __cplusplus extern "C" { #endif typedef struct ecs_sparse_t ecs_sparse_t; /** Create new sparse set */ FLECS_API ecs_sparse_t* _ecs_sparse_new( ecs_size_t elem_size); #define ecs_sparse_new(type)\ _ecs_sparse_new(sizeof(type)) /** Set id source. This allows the sparse set to use an external variable for * issuing and increasing new ids. */ FLECS_API void ecs_sparse_set_id_source( ecs_sparse_t *sparse, uint64_t *id_source); /** Free sparse set */ FLECS_API void ecs_sparse_free( ecs_sparse_t *sparse); /** Remove all elements from sparse set */ FLECS_API void ecs_sparse_clear( ecs_sparse_t *sparse); /** Add element to sparse set, this generates or recycles an id */ FLECS_API void* _ecs_sparse_add( ecs_sparse_t *sparse, ecs_size_t elem_size); #define ecs_sparse_add(sparse, type)\ ((type*)_ecs_sparse_add(sparse, sizeof(type))) /** Get last issued id. */ FLECS_API uint64_t ecs_sparse_last_id( ecs_sparse_t *sparse); /** Generate or recycle a new id. */ FLECS_API uint64_t ecs_sparse_new_id( ecs_sparse_t *sparse); /** Generate or recycle new ids in bulk. The returned pointer points directly to * the internal dense array vector with sparse ids. Operations on the sparse set * can (and likely will) modify the contents of the buffer. */ FLECS_API const uint64_t* ecs_sparse_new_ids( ecs_sparse_t *sparse, int32_t count); /** Remove an element */ FLECS_API void ecs_sparse_remove( ecs_sparse_t *sparse, uint64_t index); /** Remove an element, return pointer to the value in the sparse array */ FLECS_API void* _ecs_sparse_remove_get( ecs_sparse_t *sparse, ecs_size_t elem_size, uint64_t index); #define ecs_sparse_remove_get(sparse, type, index)\ ((type*)_ecs_sparse_remove_get(sparse, sizeof(type), index)) /** Override the generation count for a specific id */ FLECS_API void ecs_sparse_set_generation( ecs_sparse_t *sparse, uint64_t index); /** Check whether an id has ever been issued. */ FLECS_API bool ecs_sparse_exists( ecs_sparse_t *sparse, uint64_t index); /** Test if id is alive, which requires the generation count tp match. */ FLECS_API bool ecs_sparse_is_alive( const ecs_sparse_t *sparse, uint64_t index); /** Get value from sparse set by dense id. This function is useful in * combination with ecs_sparse_count for iterating all values in the set. */ FLECS_API void* _ecs_sparse_get( const ecs_sparse_t *sparse, ecs_size_t elem_size, int32_t index); #define ecs_sparse_get(sparse, type, index)\ ((type*)_ecs_sparse_get(sparse, sizeof(type), index)) /** Get the number of alive elements in the sparse set. */ FLECS_API int32_t ecs_sparse_count( const ecs_sparse_t *sparse); /** Return total number of allocated elements in the dense array */ FLECS_API int32_t ecs_sparse_size( const ecs_sparse_t *sparse); /** Get element by (sparse) id. The returned pointer is stable for the duration * of the sparse set, as it is stored in the sparse array. */ FLECS_API void* _ecs_sparse_get_sparse( const ecs_sparse_t *sparse, ecs_size_t elem_size, uint64_t index); #define ecs_sparse_get_sparse(sparse, type, index)\ ((type*)_ecs_sparse_get_sparse(sparse, sizeof(type), index)) /** Like get_sparse, but don't care whether element is alive or not. */ FLECS_API void* _ecs_sparse_get_sparse_any( ecs_sparse_t *sparse, ecs_size_t elem_size, uint64_t index); #define ecs_sparse_get_sparse_any(sparse, type, index)\ ((type*)_ecs_sparse_get_sparse_any(sparse, sizeof(type), index)) /** Get or create element by (sparse) id. */ FLECS_API void* _ecs_sparse_get_or_create( ecs_sparse_t *sparse, ecs_size_t elem_size, uint64_t index); #define ecs_sparse_get_or_create(sparse, type, index)\ ((type*)_ecs_sparse_get_or_create(sparse, sizeof(type), index)) /** Set value. */ FLECS_API void* _ecs_sparse_set( ecs_sparse_t *sparse, ecs_size_t elem_size, uint64_t index, void *value); #define ecs_sparse_set(sparse, type, index, value)\ ((type*)_ecs_sparse_set(sparse, sizeof(type), index, value)) /** Get pointer to ids (alive and not alive). Use with count() or size(). */ FLECS_API const uint64_t* ecs_sparse_ids( const ecs_sparse_t *sparse); /** Set size of the dense array. */ FLECS_API void ecs_sparse_set_size( ecs_sparse_t *sparse, int32_t elem_count); /** Copy sparse set into a new sparse set. */ FLECS_API ecs_sparse_t* ecs_sparse_copy( const ecs_sparse_t *src); /** Restore sparse set into destination sparse set. */ FLECS_API void ecs_sparse_restore( ecs_sparse_t *dst, const ecs_sparse_t *src); /** Get memory usage of sparse set. */ FLECS_API void ecs_sparse_memory( ecs_sparse_t *sparse, int32_t *allocd, int32_t *used); #ifndef FLECS_LEGACY #define ecs_sparse_each(sparse, T, var, ...)\ {\ int var##_i, var##_count = ecs_sparse_count(sparse);\ for (var##_i = 0; var##_i < var##_count; var##_i ++) {\ T* var = ecs_sparse_get(sparse, T, var##_i);\ __VA_ARGS__\ }\ } #endif #ifdef __cplusplus } #endif #endif /** Simple bitset implementation. The bitset allows for storage of arbitrary * numbers of bits. */ #ifndef FLECS_BITSET_H #define FLECS_BITSET_H #ifdef __cplusplus extern "C" { #endif typedef struct ecs_bitset_t { uint64_t *data; int32_t count; ecs_size_t size; } ecs_bitset_t; /** Initialize bitset. */ void ecs_bitset_init( ecs_bitset_t *bs); /** Deinialize bitset. */ void ecs_bitset_deinit( ecs_bitset_t *bs); /** Add n elements to bitset. */ void ecs_bitset_addn( ecs_bitset_t *bs, int32_t count); /** Ensure element exists. */ void ecs_bitset_ensure( ecs_bitset_t *bs, int32_t count); /** Set element. */ void ecs_bitset_set( ecs_bitset_t *bs, int32_t elem, bool value); /** Get element. */ bool ecs_bitset_get( const ecs_bitset_t *bs, int32_t elem); /** Return number of elements. */ int32_t ecs_bitset_count( const ecs_bitset_t *bs); /** Remove from bitset. */ void ecs_bitset_remove( ecs_bitset_t *bs, int32_t elem); /** Swap values in bitset. */ void ecs_bitset_swap( ecs_bitset_t *bs, int32_t elem_a, int32_t elem_b); #ifdef __cplusplus } #endif #endif /** Key-value datastructure. The map allows for fast retrieval of a payload for * a 64-bit key. While it is not as fast as the sparse set, it is better at * handling randomly distributed values. * * Payload is stored in bucket arrays. A bucket is computed from an id by * using the (bucket_count - 1) as an AND-mask. The number of buckets is always * a power of 2. Multiple keys will be stored in the same bucket. As a result * the worst case retrieval performance of the map is O(n), though this is rare. * On average lookup performance should equal O(1). * * The datastructure will automatically grow the number of buckets when the * ratio between elements and buckets exceeds a certain threshold (LOAD_FACTOR). * * Note that while the implementation is a hashmap, it can only compute hashes * for the provided 64 bit keys. This means that the provided keys must always * be unique. If the provided keys are hashes themselves, it is the * responsibility of the user to ensure that collisions are handled. * * In debug mode the map verifies that the type provided to the map functions * matches the one used at creation time. */ #ifndef FLECS_MAP_H #define FLECS_MAP_H #ifdef __cplusplus extern "C" { #endif typedef struct ecs_map_t ecs_map_t; typedef struct ecs_bucket_t ecs_bucket_t; typedef uint64_t ecs_map_key_t; typedef struct ecs_map_iter_t { const ecs_map_t *map; ecs_bucket_t *bucket; int32_t bucket_index; int32_t element_index; void *payload; } ecs_map_iter_t; /** Create new map. */ FLECS_API ecs_map_t * _ecs_map_new( ecs_size_t elem_size, ecs_size_t alignment, int32_t elem_count); #define ecs_map_new(T, elem_count)\ _ecs_map_new(sizeof(T), ECS_ALIGNOF(T), elem_count) /** Get element for key, returns NULL if they key doesn't exist. */ FLECS_API void * _ecs_map_get( const ecs_map_t *map, ecs_size_t elem_size, ecs_map_key_t key); #define ecs_map_get(map, T, key)\ (T*)_ecs_map_get(map, sizeof(T), (ecs_map_key_t)key) /** Get pointer element. This dereferences the map element as a pointer. This * operation returns NULL when either the element does not exist or whether the * pointer is NULL, and should therefore only be used when the application knows * for sure that a pointer should never be NULL. */ FLECS_API void * _ecs_map_get_ptr( const ecs_map_t *map, ecs_map_key_t key); #define ecs_map_get_ptr(map, T, key)\ (T)_ecs_map_get_ptr(map, key) /** Get or create element for key. */ FLECS_API void * _ecs_map_ensure( ecs_map_t *map, ecs_size_t elem_size, ecs_map_key_t key); #define ecs_map_ensure(map, T, key)\ (T*)_ecs_map_ensure(map, sizeof(T), (ecs_map_key_t)key) /** Set element. */ FLECS_API void* _ecs_map_set( ecs_map_t *map, ecs_size_t elem_size, ecs_map_key_t key, const void *payload); #define ecs_map_set(map, key, payload)\ _ecs_map_set(map, sizeof(*payload), (ecs_map_key_t)key, payload); /** Free map. */ FLECS_API void ecs_map_free( ecs_map_t *map); /** Remove key from map. */ FLECS_API void ecs_map_remove( ecs_map_t *map, ecs_map_key_t key); /** Remove all elements from map. */ FLECS_API void ecs_map_clear( ecs_map_t *map); /** Return number of elements in map. */ FLECS_API int32_t ecs_map_count( const ecs_map_t *map); /** Return number of buckets in map. */ FLECS_API int32_t ecs_map_bucket_count( const ecs_map_t *map); /** Return iterator to map contents. */ FLECS_API ecs_map_iter_t ecs_map_iter( const ecs_map_t *map); /** Obtain next element in map from iterator. */ FLECS_API void* _ecs_map_next( ecs_map_iter_t* iter, ecs_size_t elem_size, ecs_map_key_t *key); #define ecs_map_next(iter, T, key) \ (T*)_ecs_map_next(iter, sizeof(T), key) /** Obtain next pointer element from iterator. See ecs_map_get_ptr. */ FLECS_API void* _ecs_map_next_ptr( ecs_map_iter_t* iter, ecs_map_key_t *key); #define ecs_map_next_ptr(iter, T, key) \ (T)_ecs_map_next_ptr(iter, key) /** Grow number of buckets in the map for specified number of elements. */ FLECS_API void ecs_map_grow( ecs_map_t *map, int32_t elem_count); /** Set number of buckets in the map for specified number of elements. */ FLECS_API void ecs_map_set_size( ecs_map_t *map, int32_t elem_count); /** Return memory occupied by map. */ FLECS_API void ecs_map_memory( ecs_map_t *map, int32_t *allocd, int32_t *used); #ifndef FLECS_LEGACY #define ecs_map_each(map, T, key, var, ...)\ {\ ecs_map_iter_t it = ecs_map_iter(map);\ ecs_map_key_t key;\ T* var;\ (void)key;\ (void)var;\ while ((var = ecs_map_next(&it, T, &key))) {\ __VA_ARGS__\ }\ } #endif #ifdef __cplusplus } #endif /** C++ wrapper for map. */ #ifdef __cplusplus #ifndef FLECS_NO_CPP #include namespace flecs { template class map { public: map(int32_t count = 0) { init(count); } map(std::initializer_list> elems) { init(elems.size()); *this = elems; } void operator=(std::initializer_list> elems) { for (auto elem : elems) { this->set(elem.first, elem.second); } } void clear() { ecs_map_clear(m_map); } int32_t count() { return ecs_map_count(m_map); } void set(K& key, T& value) { ecs_map_set(m_map, reinterpret_cast(key), &value); } T& get(K& key) { *(T*)ecs_map_get(m_map, T, reinterpret_cast(key)); } private: void init(int32_t count) { m_map = ecs_map_new(T, count); } ecs_map_t *m_map; }; } #endif #endif #endif /* Datastructure that stores N interleaved linked lists in an array. * This allows for efficient storage of elements with mutually exclusive values. * Each linked list has a header element which points to the index in the array * that stores the first node of the list. Each list node points to the next * array element. * * The datastructure needs to be created with min and max values, so that it can * allocate an array of headers that can be directly indexed by the value. The * values are stored in a contiguous array, which allows for the values to be * iterated without having to follow the linked list nodes. * * The datastructure allows for efficient storage and retrieval for values with * mutually exclusive values, such as enumeration values. The linked list allows * an application to obtain all elements for a given (enumeration) value without * having to search. * * While the list accepts 64 bit values, it only uses the lower 32bits of the * value for selecting the correct linked list. */ #ifndef FLECS_SWITCH_LIST_H #define FLECS_SWITCH_LIST_H typedef struct ecs_switch_header_t { int32_t element; /* First element for value */ int32_t count; /* Number of elements for value */ } ecs_switch_header_t; typedef struct ecs_switch_node_t { int32_t next; /* Next node in list */ int32_t prev; /* Prev node in list */ } ecs_switch_node_t; typedef struct ecs_switch_t { uint64_t min; /* Minimum value the switch can store */ uint64_t max; /* Maximum value the switch can store */ ecs_switch_header_t *headers; /* Array with headers, indexed by value */ ecs_vector_t *nodes; /* Vector with nodes, of type ecs_switch_node_t */ ecs_vector_t *values; /* Vector with values, of type uint64_t */ } ecs_switch_t; /** Create new switch. */ ecs_switch_t* ecs_switch_new( uint64_t min, uint64_t max, int32_t elements); /** Free switch. */ void ecs_switch_free( ecs_switch_t *sw); /** Add element to switch, initialize value to 0 */ void ecs_switch_add( ecs_switch_t *sw); /** Set number of elements in switch list */ void ecs_switch_set_count( ecs_switch_t *sw, int32_t count); /** Ensure that element exists. */ void ecs_switch_ensure( ecs_switch_t *sw, int32_t count); /** Add n elements. */ void ecs_switch_addn( ecs_switch_t *sw, int32_t count); /** Set value of element. */ void ecs_switch_set( ecs_switch_t *sw, int32_t element, uint64_t value); /** Remove element. */ void ecs_switch_remove( ecs_switch_t *sw, int32_t element); /** Get value for element. */ uint64_t ecs_switch_get( const ecs_switch_t *sw, int32_t element); /** Swap element. */ void ecs_switch_swap( ecs_switch_t *sw, int32_t elem_1, int32_t elem_2); /** Get vector with all values. Use together with count(). */ ecs_vector_t* ecs_switch_values( const ecs_switch_t *sw); /** Return number of different values. */ int32_t ecs_switch_case_count( const ecs_switch_t *sw, uint64_t value); /** Return first element for value. */ int32_t ecs_switch_first( const ecs_switch_t *sw, uint64_t value); /** Return next element for value. Use with first(). */ int32_t ecs_switch_next( const ecs_switch_t *sw, int32_t elem); #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif #endif #ifndef FLECS_STRBUF_H_ #define FLECS_STRBUF_H_ #ifdef __cplusplus extern "C" { #endif #define ECS_STRBUF_INIT (ecs_strbuf_t){0} #define ECS_STRBUF_ELEMENT_SIZE (511) #define ECS_STRBUF_MAX_LIST_DEPTH (32) /* A buffer builds up a list of elements which individually can be up to N bytes * large. While appending, data is added to these elements. More elements are * added on the fly when needed. When an application calls ecs_strbuf_get, all * elements are combined in one string and the element administration is freed. * * This approach prevents reallocs of large blocks of memory, and therefore * copying large blocks of memory when appending to a large buffer. A buffer * preallocates some memory for the element overhead so that for small strings * there is hardly any overhead, while for large strings the overhead is offset * by the reduced time spent on copying memory. */ typedef struct ecs_strbuf_element { bool buffer_embedded; int32_t pos; char *buf; struct ecs_strbuf_element *next; } ecs_strbuf_element; typedef struct ecs_strbuf_element_embedded { ecs_strbuf_element super; char buf[ECS_STRBUF_ELEMENT_SIZE + 1]; } ecs_strbuf_element_embedded; typedef struct ecs_strbuf_element_str { ecs_strbuf_element super; char *alloc_str; } ecs_strbuf_element_str; typedef struct ecs_strbuf_list_elem { int32_t count; const char *separator; } ecs_strbuf_list_elem; typedef struct ecs_strbuf_t { /* When set by an application, append will write to this buffer */ char *buf; /* The maximum number of characters that may be printed */ int32_t max; /* Size of elements minus current element */ int32_t size; /* The number of elements in use */ int32_t elementCount; /* Always allocate at least one element */ ecs_strbuf_element_embedded firstElement; /* The current element being appended to */ ecs_strbuf_element *current; /* Stack that keeps track of number of list elements, used for conditionally * inserting a separator */ ecs_strbuf_list_elem list_stack[ECS_STRBUF_MAX_LIST_DEPTH]; int32_t list_sp; } ecs_strbuf_t; /* Append format string to a buffer. * Returns false when max is reached, true when there is still space */ FLECS_API bool ecs_strbuf_append( ecs_strbuf_t *buffer, const char *fmt, ...); /* Append format string with argument list to a buffer. * Returns false when max is reached, true when there is still space */ FLECS_API bool ecs_strbuf_vappend( ecs_strbuf_t *buffer, const char *fmt, va_list args); /* Append string to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API bool ecs_strbuf_appendstr( ecs_strbuf_t *buffer, const char *str); /* Append source buffer to destination buffer. * Returns false when max is reached, true when there is still space */ FLECS_API bool ecs_strbuf_mergebuff( ecs_strbuf_t *dst_buffer, ecs_strbuf_t *src_buffer); /* Append string to buffer, transfer ownership to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API bool ecs_strbuf_appendstr_zerocpy( ecs_strbuf_t *buffer, char *str); /* Append string to buffer, do not free/modify string. * Returns false when max is reached, true when there is still space */ FLECS_API bool ecs_strbuf_appendstr_zerocpy_const( ecs_strbuf_t *buffer, const char *str); /* Append n characters to buffer. * Returns false when max is reached, true when there is still space */ FLECS_API bool ecs_strbuf_appendstrn( ecs_strbuf_t *buffer, const char *str, int32_t n); /* Return result string (also resets buffer) */ FLECS_API char *ecs_strbuf_get( ecs_strbuf_t *buffer); /* Reset buffer without returning a string */ FLECS_API void ecs_strbuf_reset( ecs_strbuf_t *buffer); /* Push a list */ FLECS_API void ecs_strbuf_list_push( ecs_strbuf_t *buffer, const char *list_open, const char *separator); /* Pop a new list */ FLECS_API void ecs_strbuf_list_pop( ecs_strbuf_t *buffer, const char *list_close); /* Insert a new element in list */ FLECS_API void ecs_strbuf_list_next( ecs_strbuf_t *buffer); /* Append formatted string as a new element in list */ FLECS_API bool ecs_strbuf_list_append( ecs_strbuf_t *buffer, const char *fmt, ...); /* Append string as a new element in list */ FLECS_API bool ecs_strbuf_list_appendstr( ecs_strbuf_t *buffer, const char *str); #ifdef __cplusplus } #endif #endif #ifndef FLECS_OS_API_H #define FLECS_OS_API_H #include #if defined(_MSC_VER) || defined(__MINGW32__) #include #elif defined(__FreeBSD__) #include #else #include #endif #if defined(_WIN32) #define ECS_OS_WINDOWS #elif defined(__linux__) #define ECS_OS_LINUX #elif defined(__APPLE__) && defined(__MACH__) #define ECS_OS_DARWIN #else /* Unknown OS */ #endif #ifdef __cplusplus extern "C" { #endif typedef struct ecs_time_t { uint32_t sec; uint32_t nanosec; } ecs_time_t; /* Allocation counters (not thread safe) */ extern int64_t ecs_os_api_malloc_count; extern int64_t ecs_os_api_realloc_count; extern int64_t ecs_os_api_calloc_count; extern int64_t ecs_os_api_free_count; /* Use handle types that _at least_ can store pointers */ typedef uintptr_t ecs_os_thread_t; typedef uintptr_t ecs_os_cond_t; typedef uintptr_t ecs_os_mutex_t; typedef uintptr_t ecs_os_dl_t; /* Generic function pointer type */ typedef void (*ecs_os_proc_t)(void); /* OS API init */ typedef void (*ecs_os_api_init_t)(void); /* OS API deinit */ typedef void (*ecs_os_api_fini_t)(void); /* Memory management */ typedef void* (*ecs_os_api_malloc_t)( ecs_size_t size); typedef void (*ecs_os_api_free_t)( void *ptr); typedef void* (*ecs_os_api_realloc_t)( void *ptr, ecs_size_t size); typedef void* (*ecs_os_api_calloc_t)( ecs_size_t size); typedef char* (*ecs_os_api_strdup_t)( const char *str); /* Threads */ typedef void* (*ecs_os_thread_callback_t)( void*); typedef ecs_os_thread_t (*ecs_os_api_thread_new_t)( ecs_os_thread_callback_t callback, void *param); typedef void* (*ecs_os_api_thread_join_t)( ecs_os_thread_t thread); /* Atomic increment / decrement */ typedef int (*ecs_os_api_ainc_t)( int32_t *value); /* Mutex */ typedef ecs_os_mutex_t (*ecs_os_api_mutex_new_t)( void); typedef void (*ecs_os_api_mutex_lock_t)( ecs_os_mutex_t mutex); typedef void (*ecs_os_api_mutex_unlock_t)( ecs_os_mutex_t mutex); typedef void (*ecs_os_api_mutex_free_t)( ecs_os_mutex_t mutex); /* Condition variable */ typedef ecs_os_cond_t (*ecs_os_api_cond_new_t)( void); typedef void (*ecs_os_api_cond_free_t)( ecs_os_cond_t cond); typedef void (*ecs_os_api_cond_signal_t)( ecs_os_cond_t cond); typedef void (*ecs_os_api_cond_broadcast_t)( ecs_os_cond_t cond); typedef void (*ecs_os_api_cond_wait_t)( ecs_os_cond_t cond, ecs_os_mutex_t mutex); typedef void (*ecs_os_api_sleep_t)( int32_t sec, int32_t nanosec); typedef void (*ecs_os_api_get_time_t)( ecs_time_t *time_out); /* Logging */ typedef void (*ecs_os_api_log_t)( const char *fmt, va_list args); /* Application termination */ typedef void (*ecs_os_api_abort_t)( void); /* Dynamic libraries */ typedef ecs_os_dl_t (*ecs_os_api_dlopen_t)( const char *libname); typedef ecs_os_proc_t (*ecs_os_api_dlproc_t)( ecs_os_dl_t lib, const char *procname); typedef void (*ecs_os_api_dlclose_t)( ecs_os_dl_t lib); typedef char* (*ecs_os_api_module_to_path_t)( const char *module_id); /* Prefix members of struct with 'ecs_' as some system headers may define * macro's for functions like "strdup", "log" or "_free" */ typedef struct ecs_os_api_t { /* API init / deinit */ ecs_os_api_init_t init_; ecs_os_api_fini_t fini_; /* Memory management */ ecs_os_api_malloc_t malloc_; ecs_os_api_realloc_t realloc_; ecs_os_api_calloc_t calloc_; ecs_os_api_free_t free_; /* Strings */ ecs_os_api_strdup_t strdup_; /* Threads */ ecs_os_api_thread_new_t thread_new_; ecs_os_api_thread_join_t thread_join_; /* Atomic incremenet / decrement */ ecs_os_api_ainc_t ainc_; ecs_os_api_ainc_t adec_; /* Mutex */ ecs_os_api_mutex_new_t mutex_new_; ecs_os_api_mutex_free_t mutex_free_; ecs_os_api_mutex_lock_t mutex_lock_; ecs_os_api_mutex_lock_t mutex_unlock_; /* Condition variable */ ecs_os_api_cond_new_t cond_new_; ecs_os_api_cond_free_t cond_free_; ecs_os_api_cond_signal_t cond_signal_; ecs_os_api_cond_broadcast_t cond_broadcast_; ecs_os_api_cond_wait_t cond_wait_; /* Time */ ecs_os_api_sleep_t sleep_; ecs_os_api_get_time_t get_time_; /* Logging */ ecs_os_api_log_t log_; ecs_os_api_log_t log_error_; ecs_os_api_log_t log_debug_; ecs_os_api_log_t log_warning_; /* Application termination */ ecs_os_api_abort_t abort_; /* Dynamic library loading */ ecs_os_api_dlopen_t dlopen_; ecs_os_api_dlproc_t dlproc_; ecs_os_api_dlclose_t dlclose_; /* Overridable function that translates from a logical module id to a * shared library filename */ ecs_os_api_module_to_path_t module_to_dl_; /* Overridable function that translates from a logical module id to a * path that contains module-specif resources or assets */ ecs_os_api_module_to_path_t module_to_etc_; } ecs_os_api_t; FLECS_API extern ecs_os_api_t ecs_os_api; FLECS_API void ecs_os_init(void); FLECS_API void ecs_os_fini(void); FLECS_API void ecs_os_set_api( ecs_os_api_t *os_api); FLECS_API void ecs_os_set_api_defaults(void); /* Memory management */ #define ecs_os_malloc(size) ecs_os_api.malloc_(size); #define ecs_os_free(ptr) ecs_os_api.free_(ptr); #define ecs_os_realloc(ptr, size) ecs_os_api.realloc_(ptr, size) #define ecs_os_calloc(size) ecs_os_api.calloc_(size) #if defined(_MSC_VER) || defined(__MINGW32__) #define ecs_os_alloca(size) _alloca((size_t)(size)) #else #define ecs_os_alloca(size) alloca((size_t)(size)) #endif /* Strings */ #define ecs_os_strdup(str) ecs_os_api.strdup_(str) #define ecs_os_strlen(str) (ecs_size_t)strlen(str) #define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #define ecs_os_strncmp(str1, str2, num) strncmp(str1, str2, (size_t)(num)) #define ecs_os_memcmp(ptr1, ptr2, num) memcmp(ptr1, ptr2, (size_t)(num)) #define ecs_os_memcpy(ptr1, ptr2, num) memcpy(ptr1, ptr2, (size_t)(num)) #define ecs_os_memset(ptr, value, num) memset(ptr, value, (size_t)(num)) #define ecs_os_memmove(ptr, value, num) memmove(ptr, value, (size_t)(num)) #if defined(_MSC_VER) #define ecs_os_strcat(str1, str2) strcat_s(str1, INT_MAX, str2) #define ecs_os_sprintf(ptr, ...) sprintf_s(ptr, INT_MAX, __VA_ARGS__) #define ecs_os_vsprintf(ptr, fmt, args) vsprintf_s(ptr, INT_MAX, fmt, args) #define ecs_os_strcpy(str1, str2) strcpy_s(str1, INT_MAX, str2) #define ecs_os_strncpy(str1, str2, num) strncpy_s(str1, INT_MAX, str2, (size_t)(num)) #else #define ecs_os_strcat(str1, str2) strcat(str1, str2) #define ecs_os_sprintf(ptr, ...) sprintf(ptr, __VA_ARGS__) #define ecs_os_vsprintf(ptr, fmt, args) vsprintf(ptr, fmt, args) #define ecs_os_strcpy(str1, str2) strcpy(str1, str2) #define ecs_os_strncpy(str1, str2, num) strncpy(str1, str2, (size_t)(num)) #endif /* Threads */ #define ecs_os_thread_new(callback, param) ecs_os_api.thread_new_(callback, param) #define ecs_os_thread_join(thread) ecs_os_api.thread_join_(thread) /* Atomic increment / decrement */ #define ecs_os_ainc(value) ecs_os_api.ainc_(value) #define ecs_os_adec(value) ecs_os_api.adec_(value) /* Mutex */ #define ecs_os_mutex_new() ecs_os_api.mutex_new_() #define ecs_os_mutex_free(mutex) ecs_os_api.mutex_free_(mutex) #define ecs_os_mutex_lock(mutex) ecs_os_api.mutex_lock_(mutex) #define ecs_os_mutex_unlock(mutex) ecs_os_api.mutex_unlock_(mutex) /* Condition variable */ #define ecs_os_cond_new() ecs_os_api.cond_new_() #define ecs_os_cond_free(cond) ecs_os_api.cond_free_(cond) #define ecs_os_cond_signal(cond) ecs_os_api.cond_signal_(cond) #define ecs_os_cond_broadcast(cond) ecs_os_api.cond_broadcast_(cond) #define ecs_os_cond_wait(cond, mutex) ecs_os_api.cond_wait_(cond, mutex) /* Time */ #define ecs_os_sleep(sec, nanosec) ecs_os_api.sleep_(sec, nanosec) #define ecs_os_get_time(time_out) ecs_os_api.get_time_(time_out) /* Logging (use functions to avoid using variadic macro arguments) */ FLECS_API void ecs_os_log(const char *fmt, ...); FLECS_API void ecs_os_warn(const char *fmt, ...); FLECS_API void ecs_os_err(const char *fmt, ...); FLECS_API void ecs_os_dbg(const char *fmt, ...); /* Application termination */ #define ecs_os_abort() ecs_os_api.abort_() /* Dynamic libraries */ #define ecs_os_dlopen(libname) ecs_os_api.dlopen_(libname) #define ecs_os_dlproc(lib, procname) ecs_os_api.dlproc_(lib, procname) #define ecs_os_dlclose(lib) ecs_os_api.dlclose_(lib) /* Module id translation */ #define ecs_os_module_to_dl(lib) ecs_os_api.module_to_dl_(lib) #define ecs_os_module_to_etc(lib) ecs_os_api.module_to_etc_(lib) /* Sleep with floating point time */ FLECS_API void ecs_sleepf( double t); /* Measure time since provided timestamp */ FLECS_API double ecs_time_measure( ecs_time_t *start); /* Calculate difference between two timestamps */ FLECS_API ecs_time_t ecs_time_sub( ecs_time_t t1, ecs_time_t t2); /* Convert time value to a double */ FLECS_API double ecs_time_to_double( ecs_time_t t); FLECS_API void* ecs_os_memdup( const void *src, ecs_size_t size); /** Are heap functions available? */ FLECS_API bool ecs_os_has_heap(void); /** Are threading functions available? */ FLECS_API bool ecs_os_has_threading(void); /** Are time functions available? */ FLECS_API bool ecs_os_has_time(void); /** Are logging functions available? */ FLECS_API bool ecs_os_has_logging(void); /** Are dynamic library functions available? */ FLECS_API bool ecs_os_has_dl(void); /** Are module path functions available? */ FLECS_API bool ecs_os_has_modules(void); #ifdef __cplusplus } #endif #endif #ifdef __cplusplus extern "C" { #endif /** * @defgroup api_types Basic API types * @{ */ /** An entity identifier. */ typedef uint64_t ecs_entity_t; /** A vector containing component identifiers used to describe a type. */ typedef const ecs_vector_t* ecs_type_t; /** An ECS world is the container for all ECS data and supporting features. */ typedef struct ecs_world_t ecs_world_t; /** A snapshot stores the state of a world in a particular point in time. */ typedef struct ecs_snapshot_t ecs_snapshot_t; /** Queries are the primary mechanism for iterating (prematched) entities. */ typedef struct ecs_query_t ecs_query_t; /* An iterator lets an application iterate entities across tables. */ typedef struct ecs_iter_t ecs_iter_t; /** Refs cache data that lets them access components faster than ecs_get. */ typedef struct ecs_ref_t ecs_ref_t; /** Describes how a filter should match components with a table. */ typedef enum ecs_match_kind_t { EcsMatchDefault = 0, EcsMatchAll, /**< Match all components in a type */ EcsMatchAny, /**< Match one of the components in a type */ EcsMatchExact /**< Match the type exactly */ } ecs_match_kind_t; /** Filters alllow for ad-hoc quick filtering of entity tables. */ typedef struct ecs_filter_t { ecs_type_t include; /**< Components that should match */ ecs_type_t exclude; /**< Components that should not match */ ecs_match_kind_t include_kind; /**< Match kind for include components */ ecs_match_kind_t exclude_kind; /**< Match kind for exclude components */ } ecs_filter_t; /** Type that contains information about the world. */ typedef struct ecs_world_info_t { ecs_entity_t last_component_id; /**< Last issued component entity id */ ecs_entity_t last_id; /**< Last issued entity id */ ecs_entity_t min_id; /**< First allowed entity id */ ecs_entity_t max_id; /**< Last allowed entity id */ FLECS_FLOAT delta_time_raw; /**< Raw delta time (no time scaling) */ FLECS_FLOAT delta_time; /**< Time passed to or computed by ecs_progress */ FLECS_FLOAT time_scale; /**< Time scale applied to delta_time */ FLECS_FLOAT target_fps; /**< Target fps */ FLECS_FLOAT frame_time_total; /**< Total time spent processing a frame */ FLECS_FLOAT system_time_total; /**< Total time spent in systems */ FLECS_FLOAT merge_time_total; /**< Total time spent in merges */ FLECS_FLOAT world_time_total; /**< Time elapsed in simulation */ FLECS_FLOAT world_time_total_raw; /**< Time elapsed in simulation (no scaling) */ FLECS_FLOAT sleep_err; /**< Measured sleep error */ int32_t frame_count_total; /**< Total number of frames */ int32_t merge_count_total; /**< Total number of merges */ int32_t pipeline_build_count_total; /**< Total number of pipeline builds */ int32_t systems_ran_frame; /**< Total number of systems ran in last frame */ } ecs_world_info_t; /** @} */ /** * @defgroup function_types Function Types * @{ */ /** Action callback for systems and triggers */ typedef void (*ecs_iter_action_t)( ecs_iter_t *it); typedef bool (*ecs_iter_next_action_t)( ecs_iter_t *it); /** Compare callback used for sorting */ typedef int (*ecs_compare_action_t)( ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2); /** Compare callback used for sorting */ typedef int32_t (*ecs_rank_type_action_t)( ecs_world_t *world, ecs_entity_t rank_component, ecs_type_t type); /** Initialization action for modules */ typedef void (*ecs_module_action_t)( ecs_world_t *world); /** Action callback on world exit */ typedef void (*ecs_fini_action_t)( ecs_world_t *world, void *ctx); /** * @file api_defines.h * @brief Supporting types for the public API. * * This file containstypes that are typically not used by an application but * support the public API, and therefore must be exposed. This header should not * be included by itself. */ #ifndef FLECS_API_TYPES_H #define FLECS_API_TYPES_H #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////////////////////////////////////////// //// Opaque types //////////////////////////////////////////////////////////////////////////////// /** A stage enables modification while iterating and from multiple threads */ typedef struct ecs_stage_t ecs_stage_t; /** A table is where entities and components are stored */ typedef struct ecs_table_t ecs_table_t; /** A record stores data to map an entity id to a location in a table */ typedef struct ecs_record_t ecs_record_t; /** Table column */ typedef struct ecs_column_t ecs_column_t; /** Table data */ typedef struct ecs_data_t ecs_data_t; //////////////////////////////////////////////////////////////////////////////// //// Non-opaque types //////////////////////////////////////////////////////////////////////////////// struct ecs_record_t { ecs_table_t *table; /* Identifies a type (and table) in world */ int32_t row; /* Table row of the entity */ }; /** Cached reference. */ struct ecs_ref_t { ecs_entity_t entity; /**< Entity of the reference */ ecs_entity_t component; /**< Component of the reference */ void *table; /**< Last known table */ int32_t row; /**< Last known location in table */ int32_t alloc_count; /**< Last known alloc count of table */ ecs_record_t *record; /**< Pointer to record, if in main stage */ const void *ptr; /**< Cached ptr */ }; /** Array of entity ids that, other than a type, can live on the stack */ typedef struct ecs_entities_t { ecs_entity_t *array; /**< An array with entity ids */ int32_t count; /**< The number of entities in the array */ } ecs_entities_t; typedef struct ecs_page_cursor_t { int32_t first; int32_t count; } ecs_page_cursor_t; typedef struct ecs_page_iter_t { int32_t offset; int32_t limit; int32_t remaining; } ecs_page_iter_t; /** Table specific data for iterators */ typedef struct ecs_iter_table_t { int32_t *columns; /**< Mapping from query columns to table columns */ ecs_table_t *table; /**< The current table. */ ecs_data_t *data; /**< Table component data */ ecs_entity_t *components; /**< Components in current table */ ecs_type_t *types; /**< Components in current table */ ecs_ref_t *references; /**< References to entities (from query) */ } ecs_iter_table_t; /** Scope-iterator specific data */ typedef struct ecs_scope_iter_t { ecs_filter_t filter; ecs_vector_t *tables; int32_t index; ecs_iter_table_t table; } ecs_scope_iter_t; /** Filter-iterator specific data */ typedef struct ecs_filter_iter_t { ecs_filter_t filter; ecs_sparse_t *tables; int32_t index; ecs_iter_table_t table; } ecs_filter_iter_t; /** Iterator flags used to quickly select the optimal iterator algorithm */ typedef enum ecs_query_iter_kind_t { EcsQuerySimpleIter, /**< No paging, sorting or sparse columns */ EcsQueryPagedIter, /**< Regular iterator with paging */ EcsQuerySortedIter, /**< Sorted iterator */ EcsQuerySwitchIter /**< Switch type iterator */ } ecs_query_iter_kind_t; /** Query-iterator specific data */ typedef struct ecs_query_iter_t { ecs_page_iter_t page_iter; int32_t index; int32_t sparse_smallest; int32_t sparse_first; int32_t bitset_first; } ecs_query_iter_t; /** Query-iterator specific data */ typedef struct ecs_snapshot_iter_t { ecs_filter_t filter; ecs_vector_t *tables; /* ecs_table_leaf_t */ int32_t index; ecs_iter_table_t table; } ecs_snapshot_iter_t; /** The ecs_iter_t struct allows applications to iterate tables. * Queries and filters, among others, allow an application to iterate entities * that match a certain set of components. Because of how data is stored * internally, entities with a given set of components may be stored in multiple * consecutive arrays, stored across multiple tables. The ecs_iter_t type * enables iteration across tables. */ struct ecs_iter_t { ecs_world_t *world; /**< The world */ ecs_world_t *real_world; /**< Actual world. This differs from world when using threads. */ ecs_entity_t system; /**< The current system (if applicable) */ ecs_query_iter_kind_t kind; ecs_iter_table_t *table; /**< Table related data */ ecs_query_t *query; /**< Current query being evaluated */ int32_t table_count; /**< Active table count for query */ int32_t inactive_table_count; /**< Inactive table count for query */ int32_t column_count; /**< Number of columns for system */ void *table_columns; /**< Table component data */ ecs_entity_t *entities; /**< Entity identifiers */ void *param; /**< User data (EcsContext or param argument) */ FLECS_FLOAT delta_time; /**< Time elapsed since last frame */ FLECS_FLOAT delta_system_time;/**< Time elapsed since last system invocation */ FLECS_FLOAT world_time; /**< Time elapsed since start of simulation */ int32_t frame_offset; /**< Offset relative to frame */ int32_t offset; /**< Offset relative to current table */ int32_t count; /**< Number of entities to process by system */ int32_t total_count; /**< Total number of entities in table */ ecs_entities_t *triggered_by; /**< Component(s) that triggered the system */ ecs_entity_t interrupted_by; /**< When set, system execution is interrupted */ union { ecs_scope_iter_t parent; ecs_filter_iter_t filter; ecs_query_iter_t query; ecs_snapshot_iter_t snapshot; } iter; /**< Iterator specific data */ }; typedef enum EcsMatchFailureReason { EcsMatchOk, EcsMatchNotASystem, EcsMatchSystemIsATask, EcsMatchEntityIsDisabled, EcsMatchEntityIsPrefab, EcsMatchFromSelf, EcsMatchFromOwned, EcsMatchFromShared, EcsMatchFromContainer, EcsMatchFromEntity, EcsMatchOrFromSelf, EcsMatchOrFromOwned, EcsMatchOrFromShared, EcsMatchOrFromContainer, EcsMatchNotFromSelf, EcsMatchNotFromOwned, EcsMatchNotFromShared, EcsMatchNotFromContainer, } EcsMatchFailureReason; typedef struct ecs_match_failure_t { EcsMatchFailureReason reason; int32_t column; } ecs_match_failure_t; //////////////////////////////////////////////////////////////////////////////// //// Function types //////////////////////////////////////////////////////////////////////////////// /** Constructor/destructor. Used for initializing / deinitializing components. */ typedef void (*ecs_xtor_t)( ecs_world_t *world, ecs_entity_t component, const ecs_entity_t *entity_ptr, void *ptr, size_t size, int32_t count, void *ctx); /** Copy is invoked when a component is copied into another component. */ typedef void (*ecs_copy_t)( ecs_world_t *world, ecs_entity_t component, const ecs_entity_t *dst_entity, const ecs_entity_t *src_entity, void *dst_ptr, const void *src_ptr, size_t size, int32_t count, void *ctx); /** Move is invoked when a component is moved to another component. */ typedef void (*ecs_move_t)( ecs_world_t *world, ecs_entity_t component, const ecs_entity_t *dst_entity, const ecs_entity_t *src_entity, void *dst_ptr, void *src_ptr, size_t size, int32_t count, void *ctx); #ifdef __cplusplus } #endif #endif #ifndef FLECS_API_SUPPORT_H #define FLECS_API_SUPPORT_H /** Supporting types and functions that need to be exposed either in support of * the public API or for unit tests, but that may change between minor / patch * releases. */ #ifdef __cplusplus extern "C" { #endif /** This reserves entity ids for components. Regular entity ids will start after * this constant. This affects performance of table traversal, as edges with ids * lower than this constant are looked up in an array, whereas constants higher * than this id are looked up in a map. Increasing this value can improve * performance at the cost of (significantly) higher memory usage. */ #define ECS_HI_COMPONENT_ID (256) /* Maximum number of components */ //////////////////////////////////////////////////////////////////////////////// //// Global type handles //////////////////////////////////////////////////////////////////////////////// /** Type handles to builtin components */ FLECS_API extern ecs_type_t ecs_type(EcsComponent), ecs_type(EcsComponentLifecycle), ecs_type(EcsType), ecs_type(EcsName); /** This allows passing 0 as type to functions that accept types */ #define FLECS__TNULL 0 #define FLECS__T0 0 //////////////////////////////////////////////////////////////////////////////// //// Functions used in declarative (macro) API //////////////////////////////////////////////////////////////////////////////// FLECS_API ecs_entity_t ecs_new_entity( ecs_world_t *world, ecs_entity_t e, const char *id, const char *components); FLECS_API ecs_entity_t ecs_new_component( ecs_world_t *world, ecs_entity_t e, const char *id, size_t size, size_t alignment); FLECS_API ecs_entity_t ecs_new_module( ecs_world_t *world, ecs_entity_t e, const char *name, size_t size, size_t alignment); FLECS_API ecs_entity_t ecs_new_type( ecs_world_t *world, ecs_entity_t e, const char *id, const char *components); FLECS_API ecs_entity_t ecs_new_prefab( ecs_world_t *world, ecs_entity_t e, const char *id, const char *sig); FLECS_API ecs_entity_t ecs_new_system( ecs_world_t *world, ecs_entity_t e, const char *name, ecs_entity_t phase, const char *signature, ecs_iter_action_t action); FLECS_API ecs_entity_t ecs_new_trigger( ecs_world_t *world, ecs_entity_t e, const char *name, ecs_entity_t kind, const char *component, ecs_iter_action_t action); FLECS_API ecs_entity_t ecs_new_pipeline( ecs_world_t *world, ecs_entity_t e, const char *name, const char *expr); FLECS_API char* ecs_module_path_from_c( const char *c_name); FLECS_API bool ecs_component_has_actions( ecs_world_t *world, ecs_entity_t component); //////////////////////////////////////////////////////////////////////////////// //// Signature API //////////////////////////////////////////////////////////////////////////////// typedef enum ecs_sig_inout_kind_t { EcsInOut, EcsIn, EcsOut } ecs_sig_inout_kind_t; /** Type that is used by systems to indicate where to fetch a component from */ typedef enum ecs_sig_from_kind_t { EcsFromAny, /* Get component from self (default) */ EcsFromOwned, /* Get owned component from self */ EcsFromShared, /* Get shared component from self */ EcsFromParent, /* Get component from container */ EcsFromSystem, /* Get component from system */ EcsFromEmpty, /* Get entity handle by id */ EcsFromEntity, /* Get component from other entity */ EcsCascade /* Walk component in cascading (hierarchy) order */ } ecs_sig_from_kind_t; /** Type describing an operator used in an signature of a system signature */ typedef enum ecs_sig_oper_kind_t { EcsOperAnd = 0, EcsOperOr = 1, EcsOperNot = 2, EcsOperOptional = 3, EcsOperAll = 4, EcsOperLast = 5 } ecs_sig_oper_kind_t; /** Type that describes a single column in the system signature */ typedef struct ecs_sig_column_t { ecs_sig_from_kind_t from_kind; /* Element kind (Entity, Component) */ ecs_sig_oper_kind_t oper_kind; /* Operator kind (AND, OR, NOT) */ ecs_sig_inout_kind_t inout_kind; /* Is component read or written */ union { ecs_vector_t *type; /* Used for OR operator */ ecs_entity_t component; /* Used for AND operator */ } is; ecs_entity_t source; /* Source entity (used with FromEntity) */ char *name; /* Name of column */ } ecs_sig_column_t; /** Type that stores a parsed signature */ typedef struct ecs_sig_t { const char *name; /* Optional name used for debugging */ char *expr; /* Original expression string */ ecs_vector_t *columns; /* Columns that contain parsed data */ } ecs_sig_t; /** Parse signature. */ FLECS_API void ecs_sig_init( ecs_world_t *world, const char *name, const char *expr, ecs_sig_t *sig); /** Release signature resources */ FLECS_API void ecs_sig_deinit( ecs_sig_t *sig); /** Add column to signature. */ FLECS_API int ecs_sig_add( ecs_world_t *world, ecs_sig_t *sig, ecs_sig_from_kind_t from_kind, ecs_sig_oper_kind_t oper_kind, ecs_sig_inout_kind_t access_kind, ecs_entity_t component, ecs_entity_t source, const char *arg_name); /** Create query based on signature object. */ FLECS_API ecs_query_t* ecs_query_new_w_sig( ecs_world_t *world, ecs_entity_t system, ecs_sig_t *sig); /** Get signature object from query */ FLECS_API ecs_sig_t* ecs_query_get_sig( ecs_query_t *query); #define ECS_INVALID_ENTITY (1) #define ECS_INVALID_PARAMETER (2) #define ECS_INVALID_COMPONENT_ID (3) #define ECS_INVALID_EXPRESSION (4) #define ECS_INVALID_TYPE_EXPRESSION (5) #define ECS_INVALID_SIGNATURE (6) #define ECS_UNKNOWN_COMPONENT_ID (7) #define ECS_UNKNOWN_TYPE_ID (8) #define ECS_TYPE_NOT_AN_ENTITY (9) #define ECS_MISSING_SYSTEM_CONTEXT (10) #define ECS_NOT_A_COMPONENT (11) #define ECS_INTERNAL_ERROR (12) #define ECS_MORE_THAN_ONE_PREFAB (13) #define ECS_ALREADY_DEFINED (14) #define ECS_INVALID_COMPONENT_SIZE (15) #define ECS_INVALID_COMPONENT_ALIGNMENT (16) #define ECS_OUT_OF_MEMORY (17) #define ECS_MODULE_UNDEFINED (18) #define ECS_COLUMN_INDEX_OUT_OF_RANGE (19) #define ECS_COLUMN_IS_NOT_SHARED (20) #define ECS_COLUMN_IS_SHARED (21) #define ECS_COLUMN_HAS_NO_DATA (22) #define ECS_COLUMN_TYPE_MISMATCH (23) #define ECS_INVALID_WHILE_MERGING (24) #define ECS_INVALID_WHILE_ITERATING (25) #define ECS_INVALID_FROM_WORKER (26) #define ECS_UNRESOLVED_IDENTIFIER (27) #define ECS_OUT_OF_RANGE (28) #define ECS_COLUMN_IS_NOT_SET (29) #define ECS_UNRESOLVED_REFERENCE (30) #define ECS_THREAD_ERROR (31) #define ECS_MISSING_OS_API (32) #define ECS_TYPE_TOO_LARGE (33) #define ECS_INVALID_PREFAB_CHILD_TYPE (34) #define ECS_UNSUPPORTED (35) #define ECS_NO_OUT_COLUMNS (36) #define ECS_COLUMN_ACCESS_VIOLATION (37) #define ECS_DESERIALIZE_COMPONENT_ID_CONFLICT (38) #define ECS_DESERIALIZE_COMPONENT_SIZE_CONFLICT (39) #define ECS_DESERIALIZE_FORMAT_ERROR (40) #define ECS_INVALID_REACTIVE_SIGNATURE (41) #define ECS_INCONSISTENT_COMPONENT_NAME (42) #define ECS_TYPE_CONSTRAINT_VIOLATION (43) #define ECS_COMPONENT_NOT_REGISTERED (44) #define ECS_INCONSISTENT_COMPONENT_ID (45) #define ECS_INVALID_CASE (46) #define ECS_COMPONENT_NAME_IN_USE (47) #define ECS_INCONSISTENT_NAME (48) #define ECS_INCONSISTENT_COMPONENT_ACTION (49) #define ECS_INVALID_OPERATION (50) /** Calculate offset from address */ #define ECS_OFFSET(o, offset) (void*)(((uintptr_t)(o)) + ((uintptr_t)(offset))) #ifdef __cplusplus } #endif #endif /** Internal utility functions for tracing, warnings and errors. */ #ifndef FLECS_LOG_H #define FLECS_LOG_H #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////////////////////////////////////////// //// Color macro's //////////////////////////////////////////////////////////////////////////////// #define ECS_BLACK "\033[1;30m" #define ECS_RED "\033[0;31m" #define ECS_GREEN "\033[0;32m" #define ECS_YELLOW "\033[0;33m" #define ECS_BLUE "\033[0;34m" #define ECS_MAGENTA "\033[0;35m" #define ECS_CYAN "\033[0;36m" #define ECS_WHITE "\033[1;37m" #define ECS_GREY "\033[0;37m" #define ECS_NORMAL "\033[0;49m" #define ECS_BOLD "\033[1;49m" //////////////////////////////////////////////////////////////////////////////// //// Tracing //////////////////////////////////////////////////////////////////////////////// FLECS_API void _ecs_trace( int level, const char *file, int32_t line, const char *fmt, ...); FLECS_API void _ecs_warn( const char *file, int32_t line, const char *fmt, ...); FLECS_API void _ecs_err( const char *file, int32_t line, const char *fmt, ...); FLECS_API void ecs_log_push(void); FLECS_API void ecs_log_pop(void); #ifndef FLECS_LEGACY #define ecs_trace(lvl, ...)\ _ecs_trace(lvl, __FILE__, __LINE__, __VA_ARGS__) #define ecs_warn(...)\ _ecs_warn(__FILE__, __LINE__, __VA_ARGS__) #define ecs_err(...)\ _ecs_err(__FILE__, __LINE__, __VA_ARGS__) /* If in debug mode and no tracing verbosity is defined, compile all tracing */ #if !defined(NDEBUG) && !(defined(ECS_TRACE_0) || defined(ECS_TRACE_1) || defined(ECS_TRACE_2) || defined(ECS_TRACE_3)) #define ECS_TRACE_3 #endif #ifndef NDEBUG #if defined(ECS_TRACE_3) #define ecs_trace_1(...) ecs_trace(1, __VA_ARGS__); #define ecs_trace_2(...) ecs_trace(2, __VA_ARGS__); #define ecs_trace_3(...) ecs_trace(3, __VA_ARGS__); #elif defined(ECS_TRACE_2) #define ecs_trace_1(...) ecs_trace(1, __VA_ARGS__); #define ecs_trace_2(...) ecs_trace(2, __VA_ARGS__); #define ecs_trace_3(...) #elif defined(ECS_TRACE_1) #define ecs_trace_1(...) ecs_trace(1, __VA_ARGS__); #define ecs_trace_2(...) #define ecs_trace_3(...) #endif #else #define ecs_trace_1(...) #define ecs_trace_2(...) #define ecs_trace_3(...) #endif #endif //////////////////////////////////////////////////////////////////////////////// //// Exceptions //////////////////////////////////////////////////////////////////////////////// /** Get description for error code */ FLECS_API const char* ecs_strerror( int32_t error_code); /** Abort */ FLECS_API void _ecs_abort( int32_t error_code, const char *param, const char *file, int32_t line); #define ecs_abort(error_code, param)\ _ecs_abort(error_code, param, __FILE__, __LINE__); abort() /** Assert */ FLECS_API void _ecs_assert( bool condition, int32_t error_code, const char *param, const char *condition_str, const char *file, int32_t line); #ifdef NDEBUG #define ecs_assert(condition, error_code, param) #else #define ecs_assert(condition, error_code, param)\ _ecs_assert(condition, error_code, param, #condition, __FILE__, __LINE__);\ assert(condition) #endif FLECS_API void _ecs_parser_error( const char *name, const char *expr, int64_t column, const char *fmt, ...); #ifndef FLECS_LEGACY #define ecs_parser_error(name, expr, column, ...)\ _ecs_parser_error(name, expr, column, __VA_ARGS__);\ abort() #endif #ifdef __cplusplus } #endif #endif /** * @file type.h * @brief Type API. */ #ifndef FLECS_TYPE_H #define FLECS_TYPE_H #ifdef __cplusplus extern "C" { #endif FLECS_API ecs_type_t ecs_type_from_entity( ecs_world_t *world, ecs_entity_t entity); FLECS_API ecs_entity_t ecs_type_to_entity( ecs_world_t *world, ecs_type_t type); FLECS_API char* ecs_type_str( ecs_world_t *world, ecs_type_t type); FLECS_API ecs_type_t ecs_type_from_str( ecs_world_t *world, const char *expr); FLECS_API ecs_type_t ecs_type_find( ecs_world_t *world, ecs_entity_t *array, int32_t count); FLECS_API ecs_type_t ecs_type_merge( ecs_world_t *world, ecs_type_t type, ecs_type_t type_add, ecs_type_t type_remove); FLECS_API ecs_type_t ecs_type_add( ecs_world_t *world, ecs_type_t type, ecs_entity_t entity); FLECS_API ecs_type_t ecs_type_remove( ecs_world_t *world, ecs_type_t type, ecs_entity_t entity); FLECS_API bool ecs_type_has_entity( ecs_world_t *world, ecs_type_t type, ecs_entity_t entity); FLECS_API bool ecs_type_has_type( ecs_world_t *world, ecs_type_t type, ecs_type_t has); FLECS_API bool ecs_type_owns_entity( ecs_world_t *world, ecs_type_t type, ecs_entity_t entity, bool owned); FLECS_API bool ecs_type_owns_type( ecs_world_t *world, ecs_type_t type, ecs_type_t has, bool owned); FLECS_API ecs_entity_t ecs_type_get_entity_for_xor( ecs_world_t *world, ecs_type_t type, ecs_entity_t xor_tag); FLECS_API int32_t ecs_type_index_of( ecs_type_t type, ecs_entity_t component); FLECS_API int32_t ecs_type_trait_index_of( ecs_type_t type, int32_t start_index, ecs_entity_t trait); #ifdef __cplusplus } #endif #endif /** @} */ /** * @defgroup builtin_types Builtin Types * @{ */ /** Entity name. */ typedef struct EcsName { const char *value; /**< Entity name */ const char *symbol; /**< Optional symbol name, if it differs from name */ char *alloc_value; /**< If set, value will be freed on destruction */ } EcsName; /** Component information. */ typedef struct EcsComponent { ecs_size_t size; /**< Component size */ ecs_size_t alignment; /**< Component alignment */ } EcsComponent; /** Component that stores an ecs_type_t. * This component allows for the creation of entities that represent a type, and * therefore the creation of named types. This component is typically * instantiated by ECS_TYPE. */ typedef struct EcsType { ecs_type_t type; /**< Preserved nested types */ ecs_type_t normalized; /**< Union of type and nested AND types */ } EcsType; /** Component that contains lifecycle callbacks for a component. */ typedef struct EcsComponentLifecycle { ecs_xtor_t ctor; /**< Component constructor */ ecs_xtor_t dtor; /**< Component destructor */ ecs_copy_t copy; /**< Component copy */ ecs_move_t move; /**< Component move */ void *ctx; /**< User defined context */ } EcsComponentLifecycle; /** Component used for registering component triggers */ typedef struct EcsTrigger { ecs_entity_t kind; ecs_iter_action_t action; ecs_entity_t component; ecs_entity_t self; void *ctx; } EcsTrigger; /** @} */ /** * @defgroup type_roles Type Roles * @{ */ /* Type roles are used to indicate the role of an entity in a type. If no flag * is specified, the entity is interpreted as a regular component or tag. Flags * are added to an entity by using a bitwise OR (|). An example: * * ecs_entity_t parent = ecs_new(world, 0); * ecs_entity_t child = ecs_add_entity(world, e, ECS_CHILDOF | parent); * * Type flags can also be used in type expressions, without the ECS prefix: * * ECS_ENTITY(world, Base, Position); * ECS_TYPE(world, InstanceOfBase, INSTANCEOF | Base); */ /** Role bit added to roles to differentiate between roles and generations */ #define ECS_ROLE ((uint64_t)1 << 63) /** The INSTANCEOF role indicates that the components from the entity should be * shared with the entity that instantiates the type. */ #define ECS_INSTANCEOF (ECS_ROLE | ((ecs_entity_t)0x7E << 56)) /** The CHILDOF role indicates that the entity should be treated as a parent of * the entity that instantiates the type. */ #define ECS_CHILDOF (ECS_ROLE | ((ecs_entity_t)0x7D << 56)) /** Cases are used to switch between mutually exclusive components */ #define ECS_CASE (ECS_ROLE | ((ecs_entity_t)0x7C << 56)) /** Switches allow for fast switching between mutually exclusive components */ #define ECS_SWITCH (ECS_ROLE | ((ecs_entity_t)0x7B << 56)) /** The TRAIT role indicates that the entity is a trait identifier. */ #define ECS_TRAIT (ECS_ROLE | ((ecs_entity_t)0x7A << 56)) /** Enforce that all entities of a type are present in the type. * This flag can only be used in combination with an entity that has EcsType. */ #define ECS_AND (ECS_ROLE | ((ecs_entity_t)0x79 << 56)) /** Enforce that at least one entity of a type must be present in the type. * This flag can only be used in combination with an entity that has EcsType. */ #define ECS_OR (ECS_ROLE | ((ecs_entity_t)0x78 << 56)) /** Enforce that exactly one entity of a type must be present in the type. * This flag can only be used in combination with an entity that has EcsType. * When another entity of the XOR'd type is added to an entity of this type, the * previous entity is removed from the entity. This makes XOR useful for * implementing state machines, as it allows for traversing states while * ensuring that only one state is ever active at the same time. */ #define ECS_XOR (ECS_ROLE | ((ecs_entity_t)0x77 << 56)) /** None of the entities in a type may be added to the type. * This flag can only be used in combination with an entity that has EcsType. */ #define ECS_NOT (ECS_ROLE | ((ecs_entity_t)0x76 << 56)) /** Enforce ownership of a component */ #define ECS_OWNED (ECS_ROLE | ((ecs_entity_t)0x75 << 56)) /** Track whether component is enabled or not */ #define ECS_DISABLED (ECS_ROLE | ((ecs_entity_t)0x74 << 56)) /** @} */ /** * @defgroup builtin_tags Builtin Tags * @{ */ /* Builtin tag ids */ #define EcsModule (ECS_HI_COMPONENT_ID + 0) #define EcsPrefab (ECS_HI_COMPONENT_ID + 1) #define EcsHidden (ECS_HI_COMPONENT_ID + 2) #define EcsDisabled (ECS_HI_COMPONENT_ID + 3) #define EcsDisabledIntern (ECS_HI_COMPONENT_ID + 4) #define EcsInactive (ECS_HI_COMPONENT_ID + 5) #define EcsOnDemand (ECS_HI_COMPONENT_ID + 6) #define EcsMonitor (ECS_HI_COMPONENT_ID + 7) #define EcsPipeline (ECS_HI_COMPONENT_ID + 8) /* Trigger tags */ #define EcsOnAdd (ECS_HI_COMPONENT_ID + 9) #define EcsOnRemove (ECS_HI_COMPONENT_ID + 10) /* Set system tags */ #define EcsOnSet (ECS_HI_COMPONENT_ID + 11) #define EcsUnSet (ECS_HI_COMPONENT_ID + 12) /* Builtin pipeline tags */ #define EcsPreFrame (ECS_HI_COMPONENT_ID + 13) #define EcsOnLoad (ECS_HI_COMPONENT_ID + 14) #define EcsPostLoad (ECS_HI_COMPONENT_ID + 15) #define EcsPreUpdate (ECS_HI_COMPONENT_ID + 16) #define EcsOnUpdate (ECS_HI_COMPONENT_ID + 17) #define EcsOnValidate (ECS_HI_COMPONENT_ID + 18) #define EcsPostUpdate (ECS_HI_COMPONENT_ID + 19) #define EcsPreStore (ECS_HI_COMPONENT_ID + 20) #define EcsOnStore (ECS_HI_COMPONENT_ID + 21) #define EcsPostFrame (ECS_HI_COMPONENT_ID + 22) /** Builtin entity ids */ #define EcsFlecs (ECS_HI_COMPONENT_ID + 23) #define EcsFlecsCore (ECS_HI_COMPONENT_ID + 24) #define EcsWorld (ECS_HI_COMPONENT_ID + 25) #define EcsSingleton (ECS_HI_COMPONENT_ID + 26) #define EcsWildcard (ECS_HI_COMPONENT_ID + 27) /* Value used to quickly check if component is builtin. This is used to quickly * filter out tables with builtin components (for example for ecs_delete) */ #define EcsLastInternalComponentId (ecs_typeid(EcsSystem)) /* The first user-defined component starts from this id. Ids up to this number * are reserved for builtin components */ #define EcsFirstUserComponentId (32) /* The first user-defined entity starts from this id. Ids up to this number * are reserved for builtin components */ #define EcsFirstUserEntityId (ECS_HI_COMPONENT_ID + 32) /** @} */ /** * @defgroup convenience_macros Convenience Macro's * @{ */ /* Macro's rely on variadic arguments which are C99 and above */ #ifndef FLECS_LEGACY /** Declare an extern component variable. * Use this macro in a header when defining a component identifier globally. * Must be used together with ECS_ENTITY_DECLARE. * * Example: * ECS_COMPONENT_EXTERN(Position); */ #define ECS_ENTITY_EXTERN(id)\ extern ecs_entity_t id /** Declare an entity variable outside the scope of a function. * Use this macro in a header when defining a tag identifier globally. * Must be used together with ECS_ENTITY_DEFINE. * * Example: * ECS_ENTITY_DECLARE(Position); */ #define ECS_ENTITY_DECLARE(id)\ ecs_entity_t id /** Define a component, store in variable outside of the current scope. * Use this macro in a header when defining a component identifier globally. * Must be used together with ECS_ENTITY_DECLARE. * * Example: * ECS_ENTITY_DEFINE(world, Position); */ #define ECS_ENTITY_DEFINE(world, id, ...)\ id = ecs_new_entity(world, id, #id, #__VA_ARGS__) /** Declare a named entity with a type expression. * Example: * ECS_ENTITY(world, MyEntity, Position, Velocity); */ #define ECS_ENTITY(world, id, ...)\ ecs_entity_t id = ecs_new_entity(world, 0, #id, #__VA_ARGS__);\ (void)id /** Declare a prefab with a type expression. * Example: * ECS_PREFAB(world, MyEntity, Position, Velocity); */ #define ECS_PREFAB(world, id, ...) \ ecs_entity_t id = ecs_new_prefab(world, 0, #id, #__VA_ARGS__);\ (void)id /** Declare a component. * Example: * ECS_COMPONENT(world, Position); */ #define ECS_COMPONENT(world, id) \ ECS_ENTITY_VAR(id) = ecs_new_component(world, 0, #id, sizeof(id), ECS_ALIGNOF(id));\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &FLECS__E##id, 1);\ (void)ecs_typeid(id);\ (void)ecs_type(id) /** Declare an extern component variable. * Use this macro in a header when defining a component identifier globally. * Must be used together with ECS_COMPONENT_DECLARE. * * Example: * ECS_COMPONENT_EXTERN(Position); */ #define ECS_COMPONENT_EXTERN(id)\ extern ECS_ENTITY_VAR(id);\ extern ecs_type_t ecs_type(id) /** Declare a component variable outside the scope of a function. * Use this macro in a header when defining a component identifier globally. * Must be used together with ECS_COMPONENT_DEFINE. * * Example: * ECS_COMPONENT_IMPL(Position); */ #define ECS_COMPONENT_DECLARE(id)\ ECS_ENTITY_VAR(id);\ ecs_type_t ecs_type(id) /** Define a component, store in variable outside of the current scope. * Use this macro in a header when defining a component identifier globally. * Must be used together with ECS_COMPONENT_DECLARE. * * Example: * ECS_COMPONENT_DEFINE(world, Position); */ #define ECS_COMPONENT_DEFINE(world, id)\ ecs_typeid(id) = ecs_new_component(world, ecs_typeid(id), #id, sizeof(id), ECS_ALIGNOF(id));\ ecs_type(id) = ecs_type_from_entity(world, ecs_typeid(id)) /** Declare a tag. * Example: * ECS_TAG(world, MyTag); */ #define ECS_TAG(world, id)\ ECS_ENTITY(world, id, 0);\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &id, 1);\ (void)ecs_type(id) /** Declare an extern tag variable. * Use this macro in a header when defining a tag identifier globally. * Must be used together with ECS_TAG_DECLARE. * * Example: * ECS_TAG_EXTERN(Enemy); */ #define ECS_TAG_EXTERN(id)\ extern ecs_entity_t id;\ extern ecs_type_t ecs_type(id) /** Declare a tag variable outside the scope of a function. * Use this macro in a header when defining a tag identifier globally. * Must be used together with ECS_TAG_DEFINE. * * Example: * ECS_TAG_DECLARE(Enemy); */ #define ECS_TAG_DECLARE(id)\ ecs_entity_t id;\ ecs_type_t ecs_type(id) /** Define a tag, store in variable outside of the current scope. * Use this macro in a header when defining a tag identifier globally. * Must be used together with ECS_TAG_DECLARE. * * Example: * ECS_TAG_DEFINE(world, Enemy); */ #define ECS_TAG_DEFINE(world, id)\ id = ecs_new_entity(world, id, #id, 0);\ ecs_type(id) = ecs_type_from_entity(world, id) /** Declare a type. * Example: * ECS_TYPE(world, MyType, Position, Velocity); */ #define ECS_TYPE(world, id, ...) \ ecs_entity_t id = ecs_new_type(world, 0, #id, #__VA_ARGS__);\ ECS_TYPE_VAR(id) = ecs_type_from_entity(world, id);\ (void)id;\ (void)ecs_type(id) /** Declare an extern type variable. * Use this macro in a header when defining a type globally. * Must be used together with ECS_TYPE_DECLARE. * * Example: * ECS_TYPE_EXTERN(Movable); */ #define ECS_TYPE_EXTERN(id)\ extern ecs_entity_t id;\ extern ecs_type_t ecs_type(id) /** Declare a type variable outside the scope of a function. * Use this macro in a header when defining a type globally. * Must be used together with ECS_TYPE_DEFINE. * * Example: * ECS_TYPE_DECLARE(Movable); */ #define ECS_TYPE_DECLARE(id)\ ecs_entity_t id;\ ecs_type_t ecs_type(id) /** Define a type, store in variable outside of the current scope. * Use this macro in a header when defining a type globally. * Must be used together with ECS_TYPE_DECLARE. * * Example: * ECS_TYPE_DEFINE(world, Movable, Position, Velocity); */ #define ECS_TYPE_DEFINE(world, id, ...)\ id = ecs_new_type(world, 0, #id, #__VA_ARGS__);\ ecs_type(id) = ecs_type_from_entity(world, id);\ /** Declare a constructor. * Example: * ECS_CTOR(MyType, ptr, { ptr->value = NULL; }); */ #define ECS_CTOR(type, var, ...)\ ECS_XTOR_IMPL(type, ctor, var, __VA_ARGS__) /** Declare a destructor. * Example: * ECS_DTOR(MyType, ptr, { free(ptr->value); }); */ #define ECS_DTOR(type, var, ...)\ ECS_XTOR_IMPL(type, dtor, var, __VA_ARGS__) /** Declare a copy action. * Example: * ECS_COPY(MyType, dst, src, { dst->value = strdup(src->value); }); */ #define ECS_COPY(type, dst_var, src_var, ...)\ ECS_COPY_IMPL(type, dst_var, src_var, __VA_ARGS__) /** Declare a move action. * Example: * ECS_MOVE(MyType, dst, src, { dst->value = src->value; src->value = 0; }); */ #define ECS_MOVE(type, dst_var, src_var, ...)\ ECS_MOVE_IMPL(type, dst_var, src_var, __VA_ARGS__) /* Map from typename to function name of component lifecycle action */ #define ecs_ctor(type) type##_ctor #define ecs_dtor(type) type##_dtor #define ecs_copy(type) type##_copy #define ecs_move(type) type##_move #endif /* FLECS_LEGACY */ /** @} */ /** * @defgroup world_api World API * @{ */ /** Create a new world. * A world manages all the ECS data and supporting infrastructure. Applications * must have at least one world. Entities, component and system handles are * local to a world and should not be shared between worlds. * * This operation creates a world with all builtin modules loaded. * * @return A new world object */ FLECS_API ecs_world_t* ecs_init(void); /** Same as ecs_init, but with minimal set of modules loaded. * * @return A new world object */ FLECS_API ecs_world_t* ecs_mini(void); /** Create a new world with arguments. * Same as ecs_init, but allows passing in command line arguments. These can be * used to dynamically enable flecs features to an application. Currently these * arguments are not used. * * @return A new world object */ FLECS_API ecs_world_t* ecs_init_w_args( int argc, char *argv[]); /** Delete a world. * This operation deletes the world, and everything it contains. * * @param world The world to delete. * @return Zero if successful, non-zero if failed. */ FLECS_API int ecs_fini( ecs_world_t *world); /** Register action to be executed when world is destroyed. * Fini actions are typically used when a module needs to clean up before a * world shuts down. * * @param world The world. * @param action The function to execute. * @param ctx Userdata to pass to the function */ FLECS_API void ecs_atfini( ecs_world_t *world, ecs_fini_action_t action, void *ctx); /** Register action to be executed once after frame. * Post frame actions are typically used for calling operations that cannot be * invoked during iteration, such as changing the number of threads. * * @param world The world. * @param action The function to execute. * @param ctx Userdata to pass to the function */ FLECS_API void ecs_run_post_frame( ecs_world_t *world, ecs_fini_action_t action, void *ctx); /** Register ctor, dtor, copy & move actions for component. * * @param world The world. * @param component The component id for which to register the actions * @param actions Type that contains the component actions. */ FLECS_API void ecs_set_component_actions_w_entity( ecs_world_t *world, ecs_entity_t component, EcsComponentLifecycle *actions); #ifndef FLECS_LEGACY #define ecs_set_component_actions(world, component, ...)\ ecs_set_component_actions_w_entity(world, ecs_typeid(component), &(EcsComponentLifecycle)__VA_ARGS__) #endif /** Set a world context. * This operation allows an application to register custom data with a world * that can be accessed anywhere where the application has the world object. * * @param world The world. * @param ctx A pointer to a user defined structure. */ FLECS_API void ecs_set_context( ecs_world_t *world, void *ctx); /** Get the world context. * This operation retrieves a previously set world context. * * @param world The world. * @return The context set with ecs_set_context. If no context was set, the * function returns NULL. */ FLECS_API void* ecs_get_context( ecs_world_t *world); /** Get world info. * * @param world The world. * @return Pointer to the world info. This pointer will remain valid for as long * as the world is valid. */ FLECS_API const ecs_world_info_t* ecs_get_world_info( ecs_world_t *world); /** Dimension the world for a specified number of entities. * This operation will preallocate memory in the world for the specified number * of entities. Specifying a number lower than the current number of entities in * the world will have no effect. Note that this function does not allocate * memory for components (use ecs_dim_type for that). * * @param world The world. * @param entity_count The number of entities to preallocate. */ FLECS_API void ecs_dim( ecs_world_t *world, int32_t entity_count); /** Dimension a type for a specified number of entities. * This operation will preallocate memory for a type (table) for the * specified number of entities. Specifying a number lower than the current * number of entities in the table will have no effect. * * @param world The world. * @param type Handle to the type, as obtained by ecs_type_get. * @param entity_count The number of entities to preallocate. */ FLECS_API void ecs_dim_type( ecs_world_t *world, ecs_type_t type, int32_t entity_count); /** Set a range for issueing new entity ids. * This function constrains the entity identifiers returned by ecs_new to the * specified range. This operation can be used to ensure that multiple processes * can run in the same simulation without requiring a central service that * coordinates issueing identifiers. * * If id_end is set to 0, the range is infinite. If id_end is set to a non-zero * value, it has to be larger than id_start. If id_end is set and ecs_new is * invoked after an id is issued that is equal to id_end, the application will * abort. * * @param world The world. * @param id_start The start of the range. * @param id_end The end of the range. */ FLECS_API void ecs_set_entity_range( ecs_world_t *world, ecs_entity_t id_start, ecs_entity_t id_end); /** Enable/disable range limits. * When an application is both a receiver of range-limited entities and a * producer of range-limited entities, range checking needs to be temporarily * disabled when inserting received entities. Range checking is disabled on a * stage, so setting this value is thread safe. * * @param world The world. * @param enable True if range checking should be enabled, false to disable. * @return The previous value. */ FLECS_API bool ecs_enable_range_check( ecs_world_t *world, bool enable); /** Enable world locking while in progress. * When locking is enabled, Flecs will lock the world while in progress. This * allows applications to interact with the world from other threads without * running into race conditions. * * This is a better alternative to applications putting a lock around calls to * ecs_progress, since ecs_progress can sleep when FPS control is enabled, * which is time during which other threads could perform work. * * Locking must be enabled before applications can use the ecs_lock and * ecs_unlock functions. Locking is turned off by default. * * @param world The world. * @param enable True if locking is to be enabled. * @result The previous value of the setting. */ FLECS_API bool ecs_enable_locking( ecs_world_t *world, bool enable); /** Locks the world. * See ecs_enable_locking for details. * * @param world The world. */ FLECS_API void ecs_lock( ecs_world_t *world); /** Unlocks the world. * See ecs_enable_locking for details. * * @param world The world. */ FLECS_API void ecs_unlock( ecs_world_t *world); /** Wait until world becomes available. * When a non-flecs thread needs to interact with the world, it should invoke * this function to wait until the world becomes available (as in, it is not * progressing the frame). Invoking this function guarantees that the thread * will not starve. (as opposed to simply taking the world lock). * * An application will have to invoke ecs_end_wait after this function returns. * * @param world The world. */ FLECS_API void ecs_begin_wait( ecs_world_t *world); /** Release world after calling ecs_begin_wait. * This operation should be invoked after invoking ecs_begin_wait, and will * release the world back to the thread running the main loop. * * @param world The world. */ FLECS_API void ecs_end_wait( ecs_world_t *world); /** Enable or disable tracing. * This will enable builtin tracing. For tracing to work, it will have to be * compiled in which requires defining one of the following macro's: * * ECS_TRACE_0 - All tracing is disabled * ECS_TRACE_1 - Enable tracing level 1 * ECS_TRACE_2 - Enable tracing level 2 and below * ECS_TRACE_3 - Enable tracing level 3 and below * * If no tracing level is defined and this is a debug build, ECS_TRACE_3 will * have been automatically defined. * * The provided level corresponds with the tracing level. If -1 is provided as * value, warnings are disabled. If -2 is provided, errors are disabled as well. * * @param level Desired tracing level. */ FLECS_API void ecs_tracing_enable( int level); /** @} */ /** * @defgroup creating_entities Creating Entities * @{ */ /** Create new entity id. * This operation returns an unused entity id. * * @param world The world. * @return The new entity id. */ FLECS_API ecs_entity_t ecs_new_id( ecs_world_t *world); /** Create new component id. * This operation returns a new component id. Component ids are the same as * entity ids, but can make use of the [1 .. ECS_HI_COMPONENT_ID] range. * * This operation does not recycle ids. * * @param world The world. * @return The new component id. */ FLECS_API ecs_entity_t ecs_new_component_id( ecs_world_t *world); /** Create new entity. * This operation creates a new entity with a single entity in its type. The * entity may contain type roles. This operation recycles ids. * * @param world The world. * @param entity The entity to initialize the new entity with. * @return The new entity. */ FLECS_API ecs_entity_t ecs_new_w_entity( ecs_world_t *world, ecs_entity_t entity); /** Create new entity. * This operation creates a new entity initialized with a type. This operation * recycles ids. * * @param world The world. * @param type The type to initialize the new entity with. * @return The new entity. */ FLECS_API ecs_entity_t ecs_new_w_type( ecs_world_t *world, ecs_type_t type); /** Create a new entity. * This operation creates a new entity with a single component in its type. This * operation accepts variables created with ECS_COMPONENT, ECS_TYPE and ECS_TAG. * This operation recycles ids. * * @param world The world. * @param type The component type. * @return The new entity. */ #define ecs_new(world, type)\ ecs_new_w_type(world, ecs_type(type)) /** Create N new entities. * This operation is the same as ecs_new_w_entity, but creates N entities * instead of one and does not recycle ids. * * @param world The world. * @param entity The entity. * @param count The number of entities to create. * @return The first entity id of the newly created entities. */ FLECS_API const ecs_entity_t* ecs_bulk_new_w_entity( ecs_world_t *world, ecs_entity_t entity, int32_t count); /** Create N new entities. * This operation is the same as ecs_new_w_type, but creates N entities * instead of one and does not recycle ids. * * @param world The world. * @param type The type. * @param count The number of entities to create. * @return The first entity id of the newly created entities. */ FLECS_API const ecs_entity_t* ecs_bulk_new_w_type( ecs_world_t *world, ecs_type_t type, int32_t count); /** Create N new entities and initialize components. * This operation is the same as ecs_bulk_new_w_type, but initializes components * with the provided component array. Instead of a type the operation accepts an * array of component identifiers (entities). The component arrays need to be * provided in the same order as the component identifiers. * * @param world The world. * @param components Array with component identifiers. * @param count The number of entities to create. * @param data The data arrays to initialize the components with. * @return The first entity id of the newly created entities. */ FLECS_API const ecs_entity_t* ecs_bulk_new_w_data( ecs_world_t *world, int32_t count, ecs_entities_t *component_ids, void *data); /** Create N new entities. * This operation is the same as ecs_new, but creates N entities * instead of one and does not recycle ids. * * @param world The world. * @param component The component type. * @param count The number of entities to create. * @return The first entity id of the newly created entities. */ #define ecs_bulk_new(world, component, count)\ ecs_bulk_new_w_type(world, ecs_type(component), count) /** Clone an entity * This operation clones the components of one entity into another entity. If * no destination entity is provided, a new entity will be created. Component * values are not copied unless copy_value is true. * * @param world The world. * @param dst The entity to copy the components to. * @param src The entity to copy the components from. * @param copy_value If true, the value of components will be copied to dst. * @return The destination entity. */ FLECS_API ecs_entity_t ecs_clone( ecs_world_t *world, ecs_entity_t dst, ecs_entity_t src, bool copy_value); /** @} */ /** * @defgroup adding_removing Adding & Removing * @{ */ /** Add an entity to an entity. * This operation adds a single entity to the type of an entity. Type roles may * be used in combination with the added entity. If the entity already has the * entity, this operation will have no side effects. * * @param world The world. * @param entity The entity. * @param entity_add The entity to add. */ FLECS_API void ecs_add_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t entity_add); /** Add a type to an entity. * This operation adds a type to an entity. The resulting type of the entity * will be the union of the previous type and the provided type. If the added * type did not have new components, this operation will have no side effects. * * @param world The world. * @param entity The entity. * @param type The type to add. */ FLECS_API void ecs_add_type( ecs_world_t *world, ecs_entity_t entity, ecs_type_t type); /** Add a component, type or tag to an entity. * This operation adds a type to an entity. The resulting type of the entity * will be the union of the previous type and the provided type. If the added * type did not have new components, this operation will have no side effects. * * This operation accepts variables declared by ECS_COMPONENT, ECS_TYPE and * ECS_TAG. * * @param world The world. * @param entity The entity. * @param component The component, type or tag to add. */ #define ecs_add(world, entity, component)\ ecs_add_type(world, entity, ecs_type(component)) /** Remove an entity from an entity. * This operation removes a single entity from the type of an entity. Type roles * may be used in combination with the added entity. If the entity does not have * the entity, this operation will have no side effects. * * @param world The world. * @param entity The entity. * @param entity_remove The entity to remove. */ FLECS_API void ecs_remove_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t entity_remove); /** Remove a type from an entity. * This operation removes a type to an entity. The resulting type of the entity * will be the difference of the previous type and the provided type. If the * type did not overlap with the entity type, this operation has no side effects. * * @param world The world. * @param entity The entity. * @param type The type to remove. */ FLECS_API void ecs_remove_type( ecs_world_t *world, ecs_entity_t entity, ecs_type_t type); /** Remove a component, type or tag from an entity. * This operation removes a type to an entity. The resulting type of the entity * will be the difference of the previous type and the provided type. If the * type did not overlap with the entity type, this operation has no side effects. * * This operation accepts variables declared by ECS_COMPONENT, ECS_TYPE and * ECS_TAG. * * @param world The world. * @param entity The entity. * @param component The component, type or tag to remove. */ #define ecs_remove(world, entity, type)\ ecs_remove_type(world, entity, ecs_type(type)) /** Add / remove entity from entities matching a filter. * Combination of ecs_add_entity and ecs_remove_entity. * * @param world The world. * @param entity The entity. * @param to_add The entity to add. * @param to_remove The entity to remove. */ FLECS_API void ecs_add_remove_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t to_add, ecs_entity_t to_remove); /** Add / remove type from entities matching a filter. * Combination of ecs_add_type and ecs_remove_type. * * @param world The world. * @param entity The entity. * @param to_add The type to add. * @param to_remove The type to remove. */ FLECS_API void ecs_add_remove_type( ecs_world_t *world, ecs_entity_t entity, ecs_type_t to_add, ecs_type_t to_remove); /** Add / remove component, type or tag from entity. * Combination of ecs_add and ecs_remove. * * @param world The world. * @param entity The entity. * @param to_add The component, type or tag to add. * @param to_remove The component, type or tag to remove. */ #define ecs_add_remove(world, entity, to_add, to_remove)\ ecs_add_remove_type(world, entity, ecs_type(to_add), ecs_type(to_remove)) /** @} */ /** * @defgroup enabling_disabling Enabling & Disabling components. * @{ */ /** Enable or disable component. * Enabling or disabling a component does not add or remove a component from an * entity, but prevents it from being matched with queries. This operation can * be useful when a component must be temporarily disabled without destroying * its value. It is also a more performant operation for when an application * needs to add/remove components at high frequency, as enabling/disabling is * cheaper than a regular add or remove. * * @param world The world. * @param entity The entity. * @param component The component. * @param enable True to enable the component, false to disable. */ FLECS_API void ecs_enable_component_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t component, bool enable); #define ecs_enable_component(world, entity, T, enable)\ ecs_enable_component_w_entity(world, entity, ecs_typeid(T), enable) /** Test if component is enabled. * Test whether a component is currently enabled or disabled. This operation * will return true when the entity has the component and if it has not been * disabled by ecs_enable_component. * * @param world The world. * @param entity The entity. * @param component The component. * @return True if the component is enabled, otherwise false. */ FLECS_API bool ecs_is_component_enabled_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t component); #define ecs_is_component_enabled(world, entity, T)\ ecs_is_component_enabled_w_entity(world, entity, ecs_typeid(T)) /** @} */ /** * @defgroup traits Traits * @{ */ /** Add a trait * This operation adds a trait from an entity. * * @param world The world. * @param entity The entity. * @param component The entity for which to remove the trait. * @param trait The trait to remove. */ #define ecs_add_trait(world, entity, component, trait)\ ecs_add_entity(world, entity, ecs_trait(component, trait)) /** Remove a trait * This operation removes a trait from an entity. * * @param world The world. * @param entity The entity. * @param component The entity for which to remove the trait. * @param trait The trait to remove. */ #define ecs_remove_trait(world, entity, component, trait)\ ecs_remove_entity(world, entity, ecs_trait(component, trait)) /** Test if an entity has a trait. * This operation returns true if the entity has the provided trait for the * specified component in its type. * * @param world The world. * @param entity The entity. * @param component The entity. * @param trait The entity. * @return True if the entity has the trait, false if not. */ #define ecs_has_trait(world, entity, component, trait)\ ecs_has_entity(world, entity, ecs_trait(component, trait)) #ifndef FLECS_LEGACY /** Set trait for component. * This operation adds a trait for an entity and component. Traits can be added * multiple times to the same entity, as long as it is for different components. * * Traits can be matched with systems by providing the TRAIT role to the * trait component in the system signature. A system will match multiple times * with the same entity if the trait is added for multiple components. * * * This operation can only be used with traits that are components. * * @param world The world. * @param e The entity. * @param component The component for which to add the trait. * @param trait The trait to add. */ #define ecs_set_trait(world, entity, component, trait, ...)\ ecs_set_ptr_w_entity(world, entity, ecs_trait(ecs_typeid(component), ecs_typeid(trait)), sizeof(trait), &(trait)__VA_ARGS__) /** Set tag trait for component. * This operation is similar to ecs_set_trait, but is used for trait tags. When * a trait tag is set on an entity, the trait type is not used (tags have no * type) and instead the component type is used. * * This operation can only be used with traits that are not components. * * @param world The world. * @param e The entity. * @param component The component for which to add the trait. * @param trait The trait to add. */ #define ecs_set_trait_tag(world, entity, trait, component, ...)\ ecs_set_ptr_w_entity(world, entity, ecs_trait(ecs_typeid(component), trait), sizeof(component), &(component)__VA_ARGS__) #endif /** Get trait for component. * This operation obtains the value of a trait for a componetn that has been * added by ecs_set_trait. * * @param world The world. * @param e The entity. * @param component The component to which the trait was added. * @param trait The trait that was added. */ #define ecs_get_trait(world, entity, component, trait)\ ((trait*)ecs_get_w_entity(world, entity, ecs_trait(ecs_typeid(component), ecs_typeid(trait)))) /** Get trait tag for component. * This operation obtains the value of a trait for a componetn that has been * added by ecs_set_trait. * * @param world The world. * @param e The entity. * @param trait The trait that was added. * @param component The component to which the trait was added. */ #define ecs_get_trait_tag(world, entity, trait, component)\ ((component*)ecs_get_w_entity(world, entity, ecs_trait(ecs_typeid(component), trait))) /** Get case for switch. * This operation gets the current case for the specified switch. If the current * switch is not set for the entity, the operation will return 0. * * @param world The world. * @param e The entity. * @param sw The switch for which to obtain the case. * @return The current case for the specified switch. */ FLECS_API ecs_entity_t ecs_get_case( ecs_world_t *world, ecs_entity_t e, ecs_entity_t sw); /** @} */ /** * @defgroup deleting Deleting Entities and components * @{ */ /** Clear all components. * This operation will clear all components from an entity but will not delete * the entity itself. This effectively prevents the entity id from being * recycled. * * @param world The world. * @param entity The entity. */ FLECS_API void ecs_clear( ecs_world_t *world, ecs_entity_t entity); /** Delete an entity. * This operation will delete an entity and all of its components. The entity id * will be recycled. Repeatedly calling ecs_delete without ecs_new, * ecs_new_w_entity or ecs_new_w_type will cause a memory leak as it will cause * the list with ids that can be recycled to grow unbounded. * * @param world The world. * @param entity The entity. */ FLECS_API void ecs_delete( ecs_world_t *world, ecs_entity_t entity); /** Delete children of an entity. * This operation deletes all children of a parent entity. If a parent has no * children this operation has no effect. * * @param world The world. * @param parent The parent entity. */ FLECS_API void ecs_delete_children( ecs_world_t *world, ecs_entity_t parent); /** @} */ /** * @defgroup getting Getting Components * @{ */ /** Get an immutable pointer to a component. * This operation obtains a const pointer to the requested component. The * operation accepts the component entity id. * * @param world The world. * @param entity The entity. * @param component The entity id of the component to obtain. * @return The component pointer, NULL if the entity does not have the component. */ FLECS_API const void* ecs_get_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t component); /** Get an immutable pointer to a component. * Same as ecs_get_w_entity, but accepts the typename of a component. * * @param world The world. * @param entity The entity. * @param component The component to obtain. * @return The component pointer, NULL if the entity does not have the component. */ #define ecs_get(world, entity, component)\ ((const component*)ecs_get_w_entity(world, entity, ecs_typeid(component))) /* -- Get cached pointer -- */ /** Get an immutable reference to a component. * This operation is similar to ecs_get_w_entity but it stores temporary * information in a `ecs_ref_t` value which allows subsequent lookups to be * faster. * * @param world The world. * @param ref Pointer to a ecs_ref_t value. Must be initialized. * @param entity The entity. * @param component The entity id of the component to obtain. * @return The component pointer, NULL if the entity does not have the component. */ FLECS_API const void* ecs_get_ref_w_entity( ecs_world_t *world, ecs_ref_t *ref, ecs_entity_t entity, ecs_entity_t component); /** Get an immutable reference to a component. * Same as ecs_get_ref_w_entity, but accepts the typename of a component. * * @param world The world. * @param ref Pointer to a ecs_ref_t value. Must be initialized. * @param entity The entity. * @param component The component to obtain. * @return The component pointer, NULL if the entity does not have the component. */ #define ecs_get_ref(world, ref, entity, component)\ ((const component*)ecs_get_ref_w_entity(world, ref, entity, ecs_typeid(component))) /** Get a mutable pointer to a component. * This operation is similar to ecs_get_w_entity but it returns a mutable * pointer. If this operation is invoked from inside a system, the entity will * be staged and a pointer to the staged component will be returned. * * If the entity did not yet have the component, the component will be added by * this operation. In this case the is_added out parameter will be set to true. * * @param world The world. * @param entity The entity. * @param component The entity id of the component to obtain. * @param is_added Out parameter that returns true if the component was added. * @return The component pointer. */ FLECS_API void* ecs_get_mut_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t component, bool *is_added); /** Get a mutable pointer to a component. * Same as ecs_get_mut_w_entity but accepts a component typename. * * @param world The world. * @param entity The entity. * @param component The component to obtain. * @param is_added Out parameter that returns true if the component was added. * @return The component pointer. */ #define ecs_get_mut(world, entity, component, is_added)\ ((component*)ecs_get_mut_w_entity(world, entity, ecs_typeid(component), is_added)) /** Signal that a component has been modified. * This operation allows an application to signal to Flecs that a component has * been modified. As a result, OnSet systems will be invoked. * * This operation is commonly used together with ecs_get_mut. * * @param world The world. * @param entity The entity. * @param component The entity id of the component that was modified. */ FLECS_API void ecs_modified_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t component); /** Signal that a component has been modified. * Same as ecs_modified_w_entity but accepts a component typename. * * @param world The world. * @param entity The entity. * @param component The component that was modified. */ #define ecs_modified(world, entity, component)\ ecs_modified_w_entity(world, entity, ecs_typeid(component)) /** @} */ /** * @defgroup setting Setting Components * @{ */ /** Set the value of a component. * This operation allows an application to set the value of a component. The * operation is equivalent to calling ecs_get_mut and ecs_modified. * * If the provided entity is 0, a new entity will be created. * * @param world The world. * @param entity The entity. * @param component The entity id of the component to set. * @param size The size of the pointer to the value. * @param ptr The pointer to the value. * @return The entity. A new entity if no entity was provided. */ FLECS_API ecs_entity_t ecs_set_ptr_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t component, size_t size, const void *ptr); /** Set the value of a component. * Same as ecs_set_ptr_w_entity, but accepts a component typename and * automatically determines the type size. * * @param world The world. * @param entity The entity. * @param component The component to set. * @param size The size of the pointer to the value. * @return The entity. A new entity if no entity was provided. */ #define ecs_set_ptr(world, entity, component, ptr)\ ecs_set_ptr_w_entity(world, entity, ecs_typeid(component), sizeof(component), ptr) /* Conditionally skip macro's as compound literals and variadic arguments are * not supported in C89 */ #ifndef FLECS_LEGACY /** Set the value of a component. * Same as ecs_set_ptr, but accepts a value instead of a pointer to a value. * * @param world The world. * @param entity The entity. * @param component The component to set. * @param size The size of the pointer to the value. * @return The entity. A new entity if no entity was provided. */ #define ecs_set(world, entity, component, ...)\ ecs_set_ptr_w_entity(world, entity, ecs_typeid(component), sizeof(component), &(component)__VA_ARGS__) #endif /** @} */ /** * @defgroup singleton Singleton components * @{ */ #define ecs_singleton_get(world, comp)\ ecs_get(world, ecs_typeid(comp), comp) #ifndef FLECS_LEGACY #define ecs_singleton_set(world, comp, ...)\ ecs_set(world, ecs_typeid(comp), comp, __VA_ARGS__) #endif #define ecs_singleton_get_mut(world, comp)\ ecs_get_mut(world, ecs_typeid(comp), comp, NULL) #define ecs_singleton_modified(world, comp)\ ecs_modified(world, ecs_typeid(comp), comp) /** * @defgroup testing Testing Components * @{ */ /** Test if an entity has an entity. * This operation returns true if the entity has the provided entity in its * type. * * @param world The world. * @param entity The entity. * @param to_check The entity to test for. * @return True if the entity has the entity, false if not. */ FLECS_API bool ecs_has_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t to_check); /** Test if an entity has a type. * This operation returns true if the entity has the provided type in its * type. * * @param world The world. * @param entity The entity. * @param type The type to test for. * @return True if the entity has the type, false if not. */ FLECS_API bool ecs_has_type( ecs_world_t *world, ecs_entity_t entity, ecs_type_t type); /** Test if an entity has a component, type or tag. * This operation returns true if the entity has the provided component, type or * tag in its type. * * @param world The world. * @param entity The entity. * @param type The component, type or tag to test for. * @return True if the entity has the type, false if not. */ #define ecs_has(world, entity, type)\ ecs_has_type(world, entity, ecs_type(type)) /** Test if an entity owns component, type or tag. * This operation is similar to ecs_has, but will return false if the entity * does not own the component, which is the case if the component is defined on * a base entity with an INSTANCEOF role. * * @param world The world. * @param entity The entity. * @param type The component, type or tag to test for. * @return True if the entity owns the component, type or tag, false if not. */ #define ecs_owns(world, entity, type, owned)\ ecs_type_owns_type(world, ecs_get_type(world, entity), ecs_type(type), owned) /** Test if an entity owns an entity. * This operation is similar to ecs_has, but will return false if the entity * does not own the entity, which is the case if the entity is defined on * a base entity with an INSTANCEOF role. * * @param world The world. * @param entity The entity. * @param type The entity to test for. * @return True if the entity owns the entity, false if not. */ #define ecs_owns_entity(world, entity, has, owned)\ ecs_type_owns_entity(world, ecs_get_type(world, entity), has, owned) /** @} */ /** * @defgroup metadata Entity Metadata * @{ */ /** Test whether an entity is alive. * * @param world The world. * @param e The entity. * @return True if the entity is alive, false if the entity is not alive. */ FLECS_API bool ecs_is_alive( ecs_world_t *world, ecs_entity_t e); /** Test whether an entity exists. * Similar as ecs_is_alive, but ignores entity generation count. * * @param world The world. * @param e The entity. * @return True if the entity exists, false if the entity does not exist. */ FLECS_API bool ecs_exists( ecs_world_t *world, ecs_entity_t e); /** Get the type of an entity. * * @param world The world. * @param entity The entity. * @return The type of the entity, NULL if the entity has no components. */ FLECS_API ecs_type_t ecs_get_type( ecs_world_t *world, ecs_entity_t entity); /** Get the typeid of an entity. * * @param world The world. * @param entity The entity. * @return The typeid of the entity. */ FLECS_API ecs_entity_t ecs_get_typeid( ecs_world_t *world, ecs_entity_t e); /** Get the name of an entity. * This will return the name as specified in the EcsName component. * * @param world The world. * @param entity The entity. * @return The type of the entity, NULL if the entity has no name. */ FLECS_API const char* ecs_get_name( ecs_world_t *world, ecs_entity_t entity); /** Convert type role to string. * This operation converts a type role to a string. * * @param world The world. * @param entity The entity containing the type role. * @return The type role string, or NULL if no type role is provided. */ FLECS_API const char* ecs_role_str( ecs_entity_t entity); /** Convert entity identifier to string. * This operation interprets type roles and translates them to a string. * * @param world The world. * @param entity The entity to convert to a string. * @param buffer The buffer in which to store the string. * @param buffer_len The length of the provided buffer. * @return The number of characters required to write the string. */ FLECS_API size_t ecs_entity_str( ecs_world_t *world, ecs_entity_t entity, char *buffer, size_t buffer_len); /** Get the parent of an entity. * This will return a parent of the entity that has the specified component. If * the component is 0, the operation will return the first parent that it finds * in the entity type (an entity with a CHILDOF role). * * @param world The world. * @param entity The entity. * @param component The entity id of a component that the parent must have. * @return The parent of the entity, 0 if no parent was found. */ FLECS_API ecs_entity_t ecs_get_parent_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t component); /** Get the parent of an entity. * Same as ecs_get_parent_w_entity but accepts a component typename. * * @param world The world. * @param entity The entity. * @param component A component that the parent must have. * @return The parent of the entity, 0 if no parent was found. */ #define ecs_get_parent(world, entity, component)\ ecs_get_parent_w_entity(world, entity, ecs_typeid(component)) /** Enable or disable an entity. * This operation enables or disables an entity by adding or removing the * EcsDisabled tag. A disabled entity will not be matched with any systems, * unless the system explicitly specifies the EcsDisabled tag. * * @param world The world. * @param entity The entity to enable or disable. * @param enabled true to enable the entity, false to disable. */ FLECS_API void ecs_enable( ecs_world_t *world, ecs_entity_t entity, bool enabled); /** Count entities that have an entity. * Returns the number of entities that have the specified entity. * * @param world The world. * @param entity The entity. * @return The number of entities that have the entity. */ FLECS_API int32_t ecs_count_entity( ecs_world_t *world, ecs_entity_t entity); /** Count entities that have a type. * Returns the number of entities that have the specified type. * * @param world The world. * @param type The type. * @return The number of entities that have the type. */ FLECS_API int32_t ecs_count_type( ecs_world_t *world, ecs_type_t type); /** Count entities that have a component, type or tag. * Returns the number of entities that have the specified component, type or tag. * * @param world The world. * @param type The component, type or tag. * @return The number of entities that have the component, type or tag. */ #define ecs_count(world, type)\ ecs_count_type(world, ecs_type(type)) /** Count entities that match a filter. * Returns the number of entities that match the specified filter. * * @param world The world. * @param type The type. * @return The number of entities that match the specified filter. */ FLECS_API int32_t ecs_count_w_filter( ecs_world_t *world, const ecs_filter_t *filter); /** @} */ /** * @defgroup lookup Lookups * @{ */ /** Lookup an entity by name. * Returns an entity that matches the specified name. Only looks for entities in * the current scope (root if no scope is provided). * * @param world The world. * @param name The entity name. * @return The entity with the specified name, or 0 if no entity was found. */ FLECS_API ecs_entity_t ecs_lookup( ecs_world_t *world, const char *name); /** Lookup a child entity by name. * Returns an entity that matches the specified name. Only looks for entities in * the provided parent. If no parent is provided, look in the current scope ( * root if no scope is provided). * * @param world The world. * @param name The entity name. * @return The entity with the specified name, or 0 if no entity was found. */ FLECS_API ecs_entity_t ecs_lookup_child( ecs_world_t *world, ecs_entity_t parent, const char *name); /** Lookup an entity from a path. * Lookup an entity from a provided path, relative to the provided parent. The * operation will use the provided separator to tokenize the path expression. If * the provided path contains the prefix, the search will start from the root. * * If the entity is not found in the provided parent, the operation will * continue to search in the parent of the parent, until the root is reached. If * the entity is still not found, the lookup will search in the flecs.core * scope. If the entity is not found there either, the function returns 0. * * @param world The world. * @param parent The entity from which to resolve the path. * @param path The path to resolve. * @param sep The path separator. * @param prefix The path prefix. * @return The entity if found, else 0. */ FLECS_API ecs_entity_t ecs_lookup_path_w_sep( ecs_world_t *world, ecs_entity_t parent, const char *path, const char *sep, const char *prefix); /** Lookup an entity from a path. * Same as ecs_lookup_path_w_sep, but with defaults for the separator and * prefix. These defaults are used when looking up identifiers in a type or * signature expression. * * @param world The world. * @param parent The entity from which to resolve the path. * @param path The path to resolve. * @return The entity if found, else 0. */ #define ecs_lookup_path(world, parent, path)\ ecs_lookup_path_w_sep(world, parent, path, ".", NULL) /** Lookup an entity from a full path. * Same as ecs_lookup_pat, but searches from the current scope, or root scope * if no scope is set. * * @param world The world. * @param path The path to resolve. * @return The entity if found, else 0. */ #define ecs_lookup_fullpath(world, path)\ ecs_lookup_path_w_sep(world, 0, path, ".", NULL) /** Lookup an entity by its symbol name. * This looks up an entity by the symbol name that was provided in EcsName. The * operation does not take into account scoping, which means it will search all * entities that have an EcsName. * * This operation can be useful to resolve, for example, a type by its C * identifier, which does not include the Flecs namespacing. */ FLECS_API ecs_entity_t ecs_lookup_symbol( ecs_world_t *world, const char *name); /* Add alias for entity to global scope */ FLECS_API void ecs_use( ecs_world_t *world, ecs_entity_t entity, const char *name); /** @} */ /** * @defgroup paths Paths * @{ */ /** Get a path identifier for an entity. * This operation creates a path that contains the names of the entities from * the specified parent to the provided entity, separated by the provided * separator. If no parent is provided the path will be relative to the root. If * a prefix is provided, the path will be prefixed by the prefix. * * If the parent is equal to the provided child, the operation will return an * empty string. If a nonzero component is provided, the path will be created by * looking for parents with that component. * * The returned path should be freed by the application. * * @param world The world. * @param parent The entity from which to create the path. * @param child The entity to which to create the path. * @param component The component of the parent. * @return The relative entity path. */ FLECS_API char* ecs_get_path_w_sep( ecs_world_t *world, ecs_entity_t parent, ecs_entity_t child, ecs_entity_t component, const char *sep, const char *prefix); /** Get a path identifier for an entity. * Same as ecs_get_path_w_sep, but with default values for the separator and * prefix. These defaults are used throughout Flecs whenever identifiers are * used in type or signature expressions. * * @param world The world. * @param parent The entity from which to create the path. * @param child The entity to which to create the path. * @return The relative entity path. */ #define ecs_get_path(world, parent, child)\ ecs_get_path_w_sep(world, parent, child, 0, ".", NULL) /** Get a full path for an entity. * Same as ecs_get_path, but with default values for the separator and * prefix, and the path is created from the current scope, or root if no scope * is provided. * * @param world The world. * @param child The entity to which to create the path. * @return The entity path. */ #define ecs_get_fullpath(world, child)\ ecs_get_path_w_sep(world, 0, child, 0, ".", NULL) /** Find or create entity from path. * This operation will find or create an entity from a path, and will create any * intermediate entities if required. If the entity already exists, no entities * will be created. * * If the path starts with the prefix, then the entity will be created from the * root scope. * * @param world The world. * @param parent The entity relative to which the entity should be created. * @param path The path to create the entity for. * @param sep The separator used in the path. * @param prefix The prefix used in the path. * @return The entity. */ FLECS_API ecs_entity_t ecs_new_from_path_w_sep( ecs_world_t *world, ecs_entity_t parent, const char *path, const char *sep, const char *prefix); /** Find or create entity from path. * Same as ecs_new_from_path_w_sep, but with defaults for sep and prefix. * * @param world The world. * @param parent The entity relative to which the entity should be created. * @param path The path to create the entity for. * @return The entity. */ #define ecs_new_from_path(world, parent, path)\ ecs_new_from_path_w_sep(world, parent, path, ".", NULL) /** Find or create entity from full path. * Same as ecs_new_from_path, but entity will be created from the current scope, * or root scope if no scope is set. * * @param world The world. * @param path The path to create the entity for. * @return The entity. */ #define ecs_new_from_fullpath(world, path)\ ecs_new_from_path_w_sep(world, 0, path, ".", NULL) /** Add specified path to entity. * This operation is similar to ecs_new_from_path, but will instead add the path * to an existing entity. * * If an entity already exists for the path, it will be returned instead. * * @param world The world. * @param entity The entity to which to add the path. * @param parent The entity relative to which the entity should be created. * @param path The path to create the entity for. * @param sep The separator used in the path. * @param prefix The prefix used in the path. * @return The entity. */ FLECS_API ecs_entity_t ecs_add_path_w_sep( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t parent, const char *path, const char *sep, const char *prefix); /** Add specified path to entity. * Same as ecs_add_from_path_w_sep, but with defaults for sep and prefix. * * @param world The world. * @param entity The entity to which to add the path. * @param parent The entity relative to which the entity should be created. * @param path The path to create the entity for. * @return The entity. */ #define ecs_add_path(world, entity, parent, path)\ ecs_add_path_w_sep(world, entity, parent, path, ".", NULL) /** Add specified path to entity. * Same as ecs_add_from_path, but entity will be created from the current scope, * or root scope if no scope is set. * * @param world The world. * @param entity The entity to which to add the path. * @param path The path to create the entity for. * @return The entity. */ #define ecs_add_fullpath(world, entity, path)\ ecs_add_path_w_sep(world, entity, 0, path, ".", NULL) /** @} */ /** * @defgroup scopes Scopes * @{ */ /** Does entity have children. * * @param world The world * @param entity The entity * @return True if the entity has children, false if not. */ FLECS_API int32_t ecs_get_child_count( ecs_world_t *world, ecs_entity_t entity); /** Return a scope iterator. * A scope iterator iterates over all the child entities of the specified * parent. * * @param world The world. * @param parent The parent entity for which to iterate the children. * @return The iterator. */ FLECS_API ecs_iter_t ecs_scope_iter( ecs_world_t *world, ecs_entity_t parent); /** Return a filtered scope iterator. * Same as ecs_scope_iter, but results will be filtered. * * @param world The world. * @param parent The parent entity for which to iterate the children. * @return The iterator. */ FLECS_API ecs_iter_t ecs_scope_iter_w_filter( ecs_world_t *world, ecs_entity_t parent, ecs_filter_t *filter); /** Progress the scope iterator. * This operation progresses the scope iterator to the next table. The iterator * must have been initialized with `ecs_scope_iter`. This operation must be * invoked at least once before interpreting the contents of the iterator. * * @param it The iterator * @return True if more data is available, false if not. */ FLECS_API bool ecs_scope_next( ecs_iter_t *it); /** Set the current scope. * This operation sets the scope of the current stage to the provided entity. * As a result new entities will be created in this scope, and lookups will be * relative to the provided scope. * * It is considered good practice to restore the scope to the old value. * * @param world The world. * @param scope The entity to use as scope. * @return The previous scope. */ FLECS_API ecs_entity_t ecs_set_scope( ecs_world_t *world, ecs_entity_t scope); /** Get the current scope. * Get the scope set by ecs_set_scope. If no scope is set, this operation will * return 0. * * @param world The world. * @return The current scope. */ FLECS_API ecs_entity_t ecs_get_scope( ecs_world_t *world); /** Set a name prefix for newly created entities. * This is a utility that lets C modules use prefixed names for C types and * C functions, while using names for the entity names that do not have the * prefix. The name prefix is currently only used by ECS_COMPONENT. * * @param world The world. * @param prefix The name prefix to use. * @return The previous prefix. */ FLECS_API const char* ecs_set_name_prefix( ecs_world_t *world, const char *prefix); /** @} */ /** * @defgroup filters Filters * @{ */ /** Return a filter iterator. * A filter iterator lets an application iterate over entities that match the * specified filter. If NULL is provided for the filter, the iterator will * iterate all tables in the world. * * @param world The world. * @param filter The filter. * @return An iterator that can be used with ecs_filter_next. */ FLECS_API ecs_iter_t ecs_filter_iter( ecs_world_t *world, const ecs_filter_t *filter); /** Iterate tables matched by filter. * This operation progresses the filter iterator to the next table. The * iterator must have been initialized with `ecs_filter_iter`. This operation * must be invoked at least once before interpreting the contents of the * iterator. * * @param it The iterator * @return True if more data is available, false if not. */ FLECS_API bool ecs_filter_next( ecs_iter_t *iter); /** @} */ /** * @defgroup queries Queries * @{ */ /** Create a query. * This operation creates a query. Queries are used to iterate over entities * that match a signature expression and are the fastest way to find and iterate * over entities and their components. * * Queries should be created once, and reused multiple times. While iterating a * query is a cheap operation, creating and deleting a query is expensive. The * reason for this is that queries are "prematched", which means that a query * stores state about which entities (or rather, tables) match with the query. * Building up this state happens during query creation. * * Once a query is created, matching only happens when new tables are created. * In most applications this is an infrequent process, since it only occurs when * a new combination of components is introduced. While matching is expensive, * it is importent to note that matching does not happen on a per-entity basis, * but on a per-table basis. This means that the average time spent on matching * per frame should rapidly approach zero over the lifetime of an application. * * A query provides direct access to the component arrays. When an application * creates/deletes entities or adds/removes components, these arrays can shift * component values around, or may grow in size. This can cause unexpected or * undefined behavior to occur if these operations are performed while * iterating. To prevent this from happening an application should either not * perform these operations while iterating, or use deferred operations (see * ecs_defer_begin and ecs_defer_end). * * Queries can be created and deleted dynamically. If a query was not deleted * (using ecs_query_free) before the world is deleted, it will be deleted * automatically. * * @param world The world. * @param sig The query signature expression. * @return The new query. */ FLECS_API ecs_query_t* ecs_query_new( ecs_world_t *world, const char *sig); /** Create a subquery. * A subquery is just like a regular query, except that it is matched against * the matched tables of a parent query. Reducing the number of global (normal) * queries can improve performance, as new archetypes have to be matched against * fewer queries. * * Subqueries are cheaper to create than regular queries, because the initial * set of tables they have to match against is smaller. This makes subqueries * more suitable for creating while the simulation. * * Subqueries are not registered with tables directly, and instead receive new * table notifications from their parent query. This means that there is less * administrative overhead associated with subqueries. * * Subqueries can be nested, which allows for the creation of increasingly more * specific query hierarchies that are considerably more efficient than when all * queries would be created as global queries. * * @param world The world. * @param parent The parent query. * @param sig The query signature expression. * @return The new subquery. */ FLECS_API ecs_query_t* ecs_subquery_new( ecs_world_t *world, ecs_query_t *parent, const char *sig); /** Cleanup a query. * This operation frees a query. * * @param query The query. */ FLECS_API void ecs_query_free( ecs_query_t *query); /** Return a query iterator. * A query iterator lets an application iterate over entities that match the * specified query. If a sorting function is specified, the query will check * whether a resort is required upon creating the iterator. * * Creating a query iterator is a cheap operation that does not allocate any * resources. An application does not need to deinitialize or free a query * iterator before it goes out of scope. * * To iterate the iterator, an application should use ecs_query_next to progress * the iterator and test if it has data. * * Query iteration requires an outer and an inner loop. The outer loop uses * ecs_query_next to test if new tables are available. The inner loop iterates * the entities in the table, and is usually a for loop that uses iter.count to * loop through the entities and component arrays. * * The two loops are necessary because of how data is stored internally. * Entities are grouped by the components they have, in tables. A single query * can (and often does) match with multiple tables. Because each table has its * own set of arrays, an application has to reobtain pointers to those arrays * for each matching table. * * @param query The query to iterate. * @return The query iterator. */ FLECS_API ecs_iter_t ecs_query_iter( ecs_query_t *query); /** Iterate over a query. * This operation is similar to ecs_query_iter, but starts iterating from a * specified offset, and will not iterate more than limit entities. * * @param query The query to iterate. * @param offset The number of entities to skip. * @param limit The maximum number of entities to iterate. * @return The query iterator. */ FLECS_API ecs_iter_t ecs_query_iter_page( ecs_query_t *query, int32_t offset, int32_t limit); /** Progress the query iterator. * This operation progresses the query iterator to the next table. The * iterator must have been initialized with `ecs_query_iter`. This operation * must be invoked at least once before interpreting the contents of the * iterator. * * @param iter The iterator. * @returns True if more data is available, false if not. */ FLECS_API bool ecs_query_next( ecs_iter_t *iter); /** Progress the query iterator with filter. * This operation is the same as ecs_query_next, but accepts a filter as an * argument. Entities not matching the filter will be skipped by the iterator. * * @param iter The iterator. * @param filter The filter to apply to the iterator. * @returns True if more data is available, false if not. */ FLECS_API bool ecs_query_next_w_filter( ecs_iter_t *iter, const ecs_filter_t *filter); /** Progress the query iterator for a worker thread. * This operation is similar to ecs_query_next, but provides the ability to * divide entities up across multiple worker threads. The operation accepts a * current thread id and a total thread id, which is used to determine which * subset of entities should be assigned to the current thread. * * Current should be less than total, and there should be as many as total * threads. If there are less entities in a table than there are threads, only * as many threads as there are entities will iterate that table. * * @param it The iterator. * @param current Thread id of current thread. * @param total Total number of threads. * @returns True if more data is available, false if not. */ bool ecs_query_next_worker( ecs_iter_t *it, int32_t current, int32_t total); /** Sort the output of a query. * This enables sorting of entities across matched tables. As a result of this * operation, the order of entities in the matched tables may be changed. * Resorting happens when a query iterator is obtained, and only if the table * data has changed. * * If multiple queries that match the same (sub)set of tables specify different * sorting functions, resorting is likely to happen every time an iterator is * obtained, which can significantly slow down iterations. * * The sorting function will be applied to the specified component. Resorting * only happens if that component has changed, or when the entity order in the * table has changed. If no component is provided, resorting only happens when * the entity order changes. * * @param world The world. * @param query The query. * @param component The component used to sort. * @param compare The compare function used to sort the components. */ FLECS_API void ecs_query_order_by( ecs_world_t *world, ecs_query_t *query, ecs_entity_t component, ecs_compare_action_t compare); /** Group and sort matched tables. * Similar yo ecs_query_order_by, but instead of sorting individual entities, this * operation only sorts matched tables. This can be useful of a query needs to * enforce a certain iteration order upon the tables it is iterating, for * example by giving a certain component or tag a higher priority. * * The sorting function assigns a "rank" to each type, which is then used to * sort the tables. Tables with higher ranks will appear later in the iteration. * * Resorting happens when a query iterator is obtained, and only if the set of * matched tables for a query has changed. If table sorting is enabled together * with entity sorting, table sorting takes precedence, and entities will be * sorted within each set of tables that are assigned the same rank. * * @param world The world. * @param query The query. * @param component The component used to determine the group rank. * @param rank_action The rank action. */ FLECS_API void ecs_query_group_by( ecs_world_t *world, ecs_query_t *query, ecs_entity_t component, ecs_rank_type_action_t rank_action); /** Returns whether the query data changed since the last iteration. * This operation must be invoked before obtaining the iterator, as this will * reset the changed state. The operation will return true after: * - new entities have been matched with * - matched entities were deleted * - matched components were changed * * @param query The query. * @return true if entities changed, otherwise false. */ FLECS_API bool ecs_query_changed( ecs_query_t *query); /** Returns whether query is orphaned. * When the parent query of a subquery is deleted, it is left in an orphaned * state. The only valid operation on an orphaned query is deleting it. Only * subqueries can be orphaned. * * @param query The query. * @return true if query is orphaned, otherwise false. */ FLECS_API bool ecs_query_orphaned( ecs_query_t *query); /** @} */ /** * @defgroup iterator Iterators * @{ */ /** Obtain column data. * This operation is to be used to obtain a component array for a specific * column in the system or query signature. The column is identified by the * provided index. For example, if this is the provided signature: * * Position, Velocity * * Position is at index 1, and Velocity is at index 2. * * This operation may return NULL if the column is optional, and the current * table does not have the data. Additionally, if the column points to a shared * component or a reference, the returned value should be interpreted as a * pointer instead of an array. * * The provided size must match the size of the component, otherwise the * function may fail. * * @param it The iterator. * @param size The size of the component. * @param column The index identifying the column in a signature. * @return A pointer to the column data. */ FLECS_API void* ecs_column_w_size( const ecs_iter_t *it, size_t size, int32_t column); /** Obtain column data. * This operation is similar to ecs_column_w_size, except that it accepts the * component typename. * * @param it The iterator. * @param type The typename of the component for which to obtain the data. * @param column The index identifying the column in a signature. * @return A pointer to the column data. */ #define ecs_column(it, type, column)\ ((type*)ecs_column_w_size(it, sizeof(type), column)) /** Get column index by name. * This function obtains a column index by name. This function can only be used * if a query signature contains names. * * @param it The iterator. * @param name The column name. * @return Index of the column (to be used with ecs_column_* functions). */ FLECS_API int32_t ecs_column_index_from_name( const ecs_iter_t *it, const char *name); /** Test if column is owned or not. * The following signature shows an example of one owned components and two * components that are not owned by the current entity: * * Position, PARENT:Velocity, MyEntity:Mass * * Position is an owned component. Velocity and Mass both belong to a different * entity. This operation will return false for Position, and true for Velocity * and Mass. If a component is matched from a prefab, this operation will also * return false. * * @param it The it parameter passed into the system. * @param index The index identifying the column in a system signature. * @return True if column is owned, false if column is not. */ FLECS_API bool ecs_is_owned( const ecs_iter_t *it, int32_t column); /** Obtain a single element. * This operation is similar to ecs_column, but instead of an array it obtains * a single element from a component array. The advantage of using ecs_element * is that a system can be agnostic towards whether a component is owned or not, * at the cost of some additional performance overhead. * * @param it The iterator. * @param size The component size. * @param column The index identifying the column in a signature. * @param row The current row in the table. * @return A pointer to the current element. */ FLECS_API void *ecs_element_w_size( const ecs_iter_t *it, size_t size, int32_t column, int32_t row); /** Obtain a single element. * Same as ecs_element_w_size, but allows specifying a typename instead of a * size. * * @param it The iterator. * @param type The column type. * @param column The index identifying the column in a signature. * @param row The current row in the table. * @return A pointer to the current element. */ #define ecs_element(it, type, column, row)\ ((type*)ecs_element_w_size(it, sizeof(type), column, row)) /** Obtain the source of a signature column. * This operation returns the source of a signature column. By default this will * return 0 for regular columns, but for columns where the components are * provided by entities other than the entity being iterated over, this will * return the source of the component. * * @param it Pointer to the it object passed into the system callback. * @param column The index identifying the column in a signature. * @return The source entity for the column. */ FLECS_API ecs_entity_t ecs_column_source( const ecs_iter_t *it, int32_t column); /** Obtain the entity id of the signature column. * This operation returns the entity id of the component or tag used in the * system signature. For example, when provided this signature: * * Position, Velocity * * ecs_column_entity(world, 1) will return the component handle for Position and * ecs_column_entity(world, 2) will return the componnet handle for Velocity. * * @param it The iterator. * @param column The index identifying the column in a signature. * @return The entity id of the signature column. */ FLECS_API ecs_entity_t ecs_column_entity( const ecs_iter_t *it, int32_t column); /** Obtain the type of a column from inside a system. * This operation is equivalent to ecs_column_entity, except that it returns * a type, instead of an entity handle. Invoking this function is the same as * doing: * * ecs_type_from_entity( ecs_column_entity(it, index)); * * @param it The iterator. * @param column The index identifying the column in a signature. * @return The type for the specified column, or NULL if failed. */ FLECS_API ecs_type_t ecs_column_type( const ecs_iter_t *it, int32_t column); /** Get the size of the component of the specified column. * * @param it The iterator. * @param column The column for which to obtain the size. */ FLECS_API size_t ecs_column_size( const ecs_iter_t *it, int32_t column); /** Is the column readonly. * This operation returns if the column is a readonly column. Readonly columns * are marked in the system signature with the [in] modifier. * * @param it Pointer to the it object passed into the system callback. * @param column An index identifying the column. * @return True if the column is readonly, false otherwise. */ FLECS_API bool ecs_is_readonly( const ecs_iter_t *it, int32_t column); /** Get type of table that system is currently iterating over. * This will return the type for all entities that are currently being iterated * over, until ecs_iter_next is invoked. * * @param it The iterator. * @return The type of the current table. */ FLECS_API ecs_type_t ecs_iter_type( const ecs_iter_t *it); /** Get component array from table. * In some cases an application may require access to the table component arrays * directly instead of going through the signature to table mapping. A typical * scenario where this would be used is when using a filter iterator, where * there is no signature, and thus ecs_column cannot be used. * * @param it The iterator. * @param column The index identifying the column in a table. * @return The component array corresponding to the column index. */ FLECS_API void* ecs_table_column( const ecs_iter_t *it, int32_t column); /** Get the size of a table column. * * @param it The iterator. * @param column The column for which to obtain the size. */ FLECS_API size_t ecs_table_column_size( const ecs_iter_t *it, int32_t column); /** Get the index of the table column for a component. * * @param it The iterator. * @param component The component for which to obtain the index. */ FLECS_API int32_t ecs_table_component_index( const ecs_iter_t *it, ecs_entity_t component); /** Get a strongly typed pointer to a column (owned or shared). */ #define ECS_COLUMN(it, type, id, column)\ ECS_ENTITY_VAR(type) = ecs_column_entity(it, column);\ ECS_TYPE_VAR(type) = ecs_column_type(it, column);\ type *id = ecs_column(it, type, column);\ (void)ecs_typeid(type);\ (void)ecs_type(type);\ (void)id /** Get a strongly typed pointer to a column (owned or shared). */ #define ECS_CONST_COLUMN(it, type, id, column)\ const type *id = ecs_const_column(it, type, column) /** Obtain a handle to the component of a column */ #define ECS_COLUMN_COMPONENT(it, id, column)\ ECS_ENTITY_VAR(id) = ecs_column_entity(it, column);\ ECS_TYPE_VAR(id) = ecs_column_type(it, column);\ (void)ecs_typeid(id);\ (void)ecs_type(id) /** Obtain a handle to the entity of a column */ #define ECS_COLUMN_ENTITY(it, id, column)\ ecs_entity_t id = ecs_column_entity(it, column);\ ECS_TYPE_VAR(id) = ecs_column_type(it, column);\ (void)id;\ (void)ecs_type(id) /** Utility macro for importing all handles for a module from a system column */ #define ECS_IMPORT_COLUMN(it, module, column) \ module *ecs_module_ptr(module) = ecs_column(it, module, column);\ ecs_assert(ecs_module_ptr(module) != NULL, ECS_MODULE_UNDEFINED, #module);\ ecs_assert(!ecs_is_owned(it, column), ECS_COLUMN_IS_NOT_SHARED, NULL);\ module ecs_module(module) = *ecs_module_ptr(module);\ module##ImportHandles(ecs_module(module)) /** @} */ /** * @defgroup staging Staging * @{ */ /** Begin frame. */ FLECS_API FLECS_FLOAT ecs_frame_begin( ecs_world_t *world, FLECS_FLOAT delta_time); /** End frame. */ FLECS_API void ecs_frame_end( ecs_world_t *world); /** Begin staging. * When staging is enabled, modifications to entities are stored to a stage. * This ensures that arrays are not modified while iterating. Modifications are * merged back to the "main stage" when ecs_staging_end is invoked. * * @param world The world * @return Whether world is currently staged. */ FLECS_API bool ecs_staging_begin( ecs_world_t *world); /** End staging. * If any data was staged, this operation will merge that data back to the main * stage. * * @param world The world */ FLECS_API void ecs_staging_end( ecs_world_t *world); /** Manually merge. * When automerging is set to false, an application can invoke this operation to * force merging all stages. * * @param world The world. */ FLECS_API void ecs_merge( ecs_world_t *world); /** Defer operations until end of frame. * When this operation is invoked while iterating, operations inbetween the * defer_begin and defer_end operations are executed at the end of the frame. * * This operation is thread safe. * * @param world The world. * @return true if the world was already in deferred mode, false if not. */ FLECS_API bool ecs_defer_begin( ecs_world_t *world); /** End block of operations to defer. * See defer_begin. * * This operation is thread safe. * * @param world The world. * @return true if world changed from deferred mode to non-deferred mode. */ FLECS_API bool ecs_defer_end( ecs_world_t *world); /** Enable / disable automerging. * When automerging is enabled, running a pipeline will automatically merge when * necessary. With automerging disabled, merging will not happen unless the * application manually invokes ecs_merge. * * @param world The world. */ FLECS_API void ecs_set_automerge( ecs_world_t *world, bool auto_merge); /** @} */ /* Optional modules */ #ifdef FLECS_SYSTEM #ifdef FLECS_SYSTEM #define FLECS_MODULE #ifdef FLECS_MODULE /** * @file module.h * @brief Module API. */ #ifndef FLECS_MODULE_H #define FLECS_MODULE_H #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////////////////////////////////////////// //// Module API //////////////////////////////////////////////////////////////////////////////// /** Import a module. * This operation will load a modules and store the public module handles in the * handles_out out parameter. The module name will be used to verify if the * module was already loaded, in which case it won't be reimported. The name * will be translated from PascalCase to an entity path (pascal.case) before the * lookup occurs. * * Module contents will be stored as children of the module entity. This * prevents modules from accidentally defining conflicting identifiers. This is * enforced by setting the scope before and after loading the module to the * module entity id. * * A more convenient way to import a module is by using the ECS_IMPORT macro. * * @param world The world. * @param module The module to load. * @param module_name The name of the module to load. * @param flags An integer that will be passed into the module import action. * @param handles_out A struct with handles to the module components/systems. * @param handles_size Size of the handles_out parameter. * @return The module entity. */ FLECS_API ecs_entity_t ecs_import( ecs_world_t *world, ecs_module_action_t module, const char *module_name, void *handles_out, size_t handles_size); /* Import a module from a library. * Similar to ecs_import, except that this operation will attempt to load the * module from a dynamic library. * * A library may contain multiple modules, which is why both a library name and * a module name need to be provided. If only a library name is provided, the * library name will be reused for the module name. * * The library will be looked up using a canonical name, which is in the same * form as a module, like `flecs.components.transform`. To transform this * identifier to a platform specific library name, the operation relies on the * module_to_dl callback of the os_api which the application has to override if * the default does not yield the correct library name. * * @param world The world. * @param library_name The name of the library to load. * @param module_name The name of the module to load. * @param flags The flags to pass to the module. */ FLECS_API ecs_entity_t ecs_import_from_library( ecs_world_t *world, const char *library_name, const char *module_name); /** Define module */ #define ECS_MODULE(world, id)\ ECS_ENTITY_VAR(id) = ecs_new_module(world, 0, #id, sizeof(id), ECS_ALIGNOF(id));\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &FLECS__E##id, 1);\ id *handles = (id*)ecs_get_mut(world, ecs_typeid(id), id, NULL);\ (void)ecs_typeid(id);\ (void)ecs_type(id);\ (void)handles; /** Wrapper around ecs_import. * This macro provides a convenient way to load a module with the world. It can * be used like this: * * ECS_IMPORT(world, FlecsSystemsPhysics, 0); * * This macro will define entity and type handles for the component associated * with the module. An application can retrieve the module component like this: * * FlecsSystemsPhysics m = ecs_get(world, EcsSingleton, FlecsSystemsPhysics); * * The contents of a module component are module specific, although they * typically contain handles to the content of the module. */ #define ECS_IMPORT(world, id) \ id ecs_module(id);\ char *id##__name = ecs_module_path_from_c(#id);\ ECS_ENTITY_VAR(id) = ecs_import(\ world, id##Import, id##__name, &ecs_module(id), sizeof(id));\ ecs_os_free(id##__name);\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &FLECS__E##id, 1);\ id##ImportHandles(ecs_module(id));\ (void)ecs_typeid(id);\ (void)ecs_type(id);\ /** Declare type variable */ #define ECS_TYPE_VAR(id)\ ecs_type_t ecs_type(id) /** Declare entity variable */ #define ECS_ENTITY_VAR(id)\ ecs_entity_t ecs_typeid(id) /** Utility macro for declaring a component inside a handles type */ #define ECS_DECLARE_COMPONENT(id)\ ECS_ENTITY_VAR(id);\ ECS_TYPE_VAR(id) /** Utility macro for declaring an entity inside a handles type */ #define ECS_DECLARE_ENTITY(id)\ ecs_entity_t id;\ ECS_TYPE_VAR(id) /** Utility macro for declaring a type inside a handles type */ #define ECS_DECLARE_TYPE(id)\ ECS_DECLARE_ENTITY(id) /** Utility macro for setting a component in a module function */ #define ECS_SET_COMPONENT(id)\ if (handles) handles->ecs_typeid(id) = ecs_typeid(id);\ if (handles) handles->ecs_type(id) = ecs_type(id) /** Utility macro for setting an entity in a module function */ #define ECS_SET_ENTITY(id)\ if (handles) handles->id = id; /** Utility macro for setting a type in a module function */ #define ECS_SET_TYPE(id)\ if (handles) handles->id = id;\ if (handles) handles->ecs_type(id) = ecs_type(id); #define ECS_EXPORT_COMPONENT(id)\ ECS_SET_COMPONENT(id) #define ECS_EXPORT_ENTITY(id)\ ECS_SET_ENTITY(id) #define ECS_EXPORT_TYPE(id)\ ECS_SET_TYPE(id) /** Utility macro for importing a component */ #define ECS_IMPORT_COMPONENT(handles, id)\ ECS_ENTITY_VAR(id) = (handles).ecs_typeid(id); (void)ecs_typeid(id);\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &FLECS__E##id, 1);\ (void)ecs_typeid(id);\ (void)ecs_type(id) /** Utility macro for importing an entity */ #define ECS_IMPORT_ENTITY(handles, id)\ ecs_entity_t id = (handles).id;\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &id, 1);\ (void)id;\ (void)ecs_type(id) /** Utility macro for importing a type */ #define ECS_IMPORT_TYPE(handles, id)\ ecs_entity_t id = (handles).id;\ ecs_type_t ecs_type(id) = (handles).ecs_type(id);\ (void)id;\ (void)ecs_type(id) #ifdef __cplusplus } #endif #endif #endif #ifndef FLECS_SYSTEMS_H #define FLECS_SYSTEMS_H #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////////////////////////////////////////// //// Components //////////////////////////////////////////////////////////////////////////////// FLECS_API extern ecs_type_t ecs_type(EcsTrigger), ecs_type(EcsSystem), ecs_type(EcsTickSource), ecs_type(EcsSignatureExpr), ecs_type(EcsSignature), ecs_type(EcsQuery), ecs_type(EcsIterAction), ecs_type(EcsContext); /* Component used to provide a tick source to systems */ typedef struct EcsTickSource { bool tick; /* True if providing tick */ FLECS_FLOAT time_elapsed; /* Time elapsed since last tick */ } EcsTickSource; /* Signature expression */ typedef struct EcsSignatureExpr { const char *expr; } EcsSignatureExpr; /* Parsed signature */ typedef struct EcsSignature { ecs_sig_t signature; } EcsSignature; /* Query component */ typedef struct EcsQuery { ecs_query_t *query; } EcsQuery; /* System action */ typedef struct EcsIterAction { ecs_iter_action_t action; } EcsIterAction; /* System context */ typedef struct EcsContext { const void *ctx; } EcsContext; //////////////////////////////////////////////////////////////////////////////// //// Systems API //////////////////////////////////////////////////////////////////////////////// /** Declare a systen. * This macro declares a system with the specified function, kind and signature. * Systems are matched with entities that match the system signature. The system * signature is specified as a comma-separated list of column expressions, where * a column expression can be any of the following: * * - A simple component identifier ('Position') * - An OR expression ('Position | Velocity') * - An optional expression ('?Position') * - A NOT expression ('!Position') * - An OWNED expression ('OWNED:Position') * - A SHARED expression ('SHARED:Position') * - A PARENT expression ('PARENT:Position') * - A CASCADE expression ('CASCADE:Position') * - An entity expression ('MyEntity:Position') * - An empty expression (':Position') * * The systen kind specifies the phase in which the system is ran. * * Examples: * ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity, !AngularVelocity); * ECS_SYSTEM(world, Transform, EcsPostUpdate, PARENT:Transform, Transform); * * In these examples, 'Move' and 'Transform' must be valid identifiers to a C * function of the following signature: * * void Move(ecs_iter_t *it) { ... } * * Inside this function the system can access the data from the signature with * the ECS_COLUMN macro: * * ECS_COLUMN(it, Position, p, 1); * ECS_COLUMN(it, Velocity, v, 2); * * For more details on system signatures and phases see the Flecs manual. */ #ifndef FLECS_LEGACY #define ECS_SYSTEM(world, name, kind, ...) \ ecs_iter_action_t ecs_iter_action(name) = name;\ ecs_entity_t name = ecs_new_system(world, 0, #name, kind, #__VA_ARGS__, ecs_iter_action(name));\ (void)ecs_iter_action(name);\ (void)name; #endif #define ECS_TRIGGER(world, name, kind, component) \ ecs_entity_t __F##name = ecs_new_trigger(world, 0, #name, kind, #component, name);\ ecs_entity_t name = __F##name;\ (void)__F##name;\ (void)name; /** Run a specific system manually. * This operation runs a single system manually. It is an efficient way to * invoke logic on a set of entities, as manual systems are only matched to * tables at creation time or after creation time, when a new table is created. * * Manual systems are useful to evaluate lists of prematched entities at * application defined times. Because none of the matching logic is evaluated * before the system is invoked, manual systems are much more efficient than * manually obtaining a list of entities and retrieving their components. * * An application may pass custom data to a system through the param parameter. * This data can be accessed by the system through the param member in the * ecs_iter_t value that is passed to the system callback. * * Any system may interrupt execution by setting the interrupted_by member in * the ecs_iter_t value. This is particularly useful for manual systems, where * the value of interrupted_by is returned by this operation. This, in * cominbation with the param argument lets applications use manual systems * to lookup entities: once the entity has been found its handle is passed to * interrupted_by, which is then subsequently returned. * * @param world The world. * @param system The system to run. * @param delta_time: The time passed since the last system invocation. * @param param A user-defined parameter to pass to the system. * @return handle to last evaluated entity if system was interrupted. */ FLECS_API ecs_entity_t ecs_run( ecs_world_t *world, ecs_entity_t system, FLECS_FLOAT delta_time, void *param); /** Run system with offset/limit and type filter. * This operation is the same as ecs_run, but filters the entities that will be * iterated by the system. * * Entities can be filtered in two ways. Offset and limit control the range of * entities that is iterated over. The range is applied to all entities matched * with the system, thus may cover multiple archetypes. * * The type filter controls which entity types the system will evaluate. Only * types that contain all components in the type filter will be iterated over. A * type filter is only evaluated once per table, which makes filtering cheap if * the number of entities is large and the number of tables is small, but not as * cheap as filtering in the system signature. * * @param world The world. * @param system The system to invoke. * @param delta_time: The time passed since the last system invocation. * @param filter A component or type to filter matched entities. * @param param A user-defined parameter to pass to the system. * @return handle to last evaluated entity if system was interrupted. */ FLECS_API ecs_entity_t ecs_run_w_filter( ecs_world_t *world, ecs_entity_t system, FLECS_FLOAT delta_time, int32_t offset, int32_t limit, const ecs_filter_t *filter, void *param); /** System status change callback */ typedef enum ecs_system_status_t { EcsSystemStatusNone = 0, EcsSystemEnabled, EcsSystemDisabled, EcsSystemActivated, EcsSystemDeactivated } ecs_system_status_t; typedef void (*ecs_system_status_action_t)( ecs_world_t *world, ecs_entity_t system, ecs_system_status_t status, void *ctx); /** Set system status action. * The status action is invoked whenever a system is enabled or disabled. Note * that a system may be enabled but may not actually match any entities. In this * case the system is enabled but not _active_. * * In addition to communicating the enabled / disabled status, the action also * communicates changes in the activation status of the system. A system becomes * active when it has one or more matching entities, and becomes inactive when * it no longer matches any entities. * * A system switches between enabled and disabled when an application invokes the * ecs_enable operation with a state different from the state of the system, for * example the system is disabled, and ecs_enable is invoked with enabled: true. * * Additionally a system may switch between enabled and disabled when it is an * EcsOnDemand system, and interest is generated or lost for one of its [out] * columns. * * @param world The world. * @param system The system for which to set the action. * @param action The action. * @param ctx Context that will be passed to the action when invoked. */ FLECS_API void ecs_set_system_status_action( ecs_world_t *world, ecs_entity_t system, ecs_system_status_action_t action, const void *ctx); //////////////////////////////////////////////////////////////////////////////// //// System debug API //////////////////////////////////////////////////////////////////////////////// typedef struct ecs_dbg_system_t { ecs_entity_t system; int32_t entities_matched_count; int32_t active_table_count; int32_t inactive_table_count; bool enabled; void *system_data; } ecs_dbg_system_t; FLECS_API int ecs_dbg_system( ecs_world_t *world, ecs_entity_t system, ecs_dbg_system_t *dbg_out); FLECS_API ecs_table_t* ecs_dbg_get_active_table( ecs_world_t *world, ecs_dbg_system_t *dbg, int32_t index); FLECS_API ecs_table_t* ecs_dbg_get_inactive_table( ecs_world_t *world, ecs_dbg_system_t *dbg, int32_t index); FLECS_API ecs_type_t ecs_dbg_get_column_type( ecs_world_t *world, ecs_entity_t system, int32_t column_index); FLECS_API bool ecs_dbg_match_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t system, ecs_match_failure_t *failure_info_out); //////////////////////////////////////////////////////////////////////////////// //// Module //////////////////////////////////////////////////////////////////////////////// /* Pipeline component is empty: components and tags in module are static */ typedef struct FlecsSystem { int32_t dummy; } FlecsSystem; FLECS_API void FlecsSystemImport( ecs_world_t *world); #define FlecsSystemImportHandles(handles) #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_PIPELINE #ifdef FLECS_PIPELINE #define FLECS_SYSTEM #ifndef FLECS_PIPELINE_H #define FLECS_PIPELINE_H #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////////////////////////////////////////// //// Pipeline API //////////////////////////////////////////////////////////////////////////////// #ifndef FLECS_LEGACY #define ECS_PIPELINE(world, name, ...) \ ecs_entity_t name = ecs_new_pipeline(world, 0, #name, #__VA_ARGS__); #endif /** Set a custom pipeline. * This operation sets the pipeline to run when ecs_progress is invoked. * * @param world The world. * @param pipeline The pipeline to set. */ FLECS_API void ecs_set_pipeline( ecs_world_t *world, ecs_entity_t pipeline); /** Get the current pipeline. * This operation gets the current pipeline. * * @param world The world. * @param pipeline The pipeline to set. */ FLECS_API ecs_entity_t ecs_get_pipeline( ecs_world_t *world); /** Progress a world. * This operation progresses the world by running all systems that are both * enabled and periodic on their matching entities. * * An application can pass a delta_time into the function, which is the time * passed since the last frame. This value is passed to systems so they can * update entity values proportional to the elapsed time since their last * invocation. * * When an application passes 0 to delta_time, ecs_progress will automatically * measure the time passed since the last frame. If an application does not uses * time management, it should pass a non-zero value for delta_time (1.0 is * recommended). That way, no time will be wasted measuring the time. * * @param world The world to progress. * @param delta_time The time passed since the last frame. * @return false if ecs_quit has been called, true otherwise. */ FLECS_API bool ecs_progress( ecs_world_t *world, FLECS_FLOAT delta_time); /** Set target frames per second (FPS) for application. * Setting the target FPS ensures that ecs_progress is not invoked faster than * the specified FPS. When enabled, ecs_progress tracks the time passed since * the last invocation, and sleeps the remaining time of the frame (if any). * * This feature ensures systems are ran at a consistent interval, as well as * conserving CPU time by not running systems more often than required. * * Note that ecs_progress only sleeps if there is time left in the frame. Both * time spent in flecs as time spent outside of flecs are taken into * account. * * @param world The world. * @param fps The target FPS. */ FLECS_API void ecs_set_target_fps( ecs_world_t *world, FLECS_FLOAT fps); /** Set time scale. * Increase or decrease simulation speed by the provided multiplier. * * @param world The world. * @param scale The scale to apply (default = 1). */ FLECS_API void ecs_set_time_scale( ecs_world_t *world, FLECS_FLOAT scale); /** Reset world clock. * Reset the clock that keeps track of the total time passed in the simulation. * * @param world The world. */ FLECS_API void ecs_reset_clock( ecs_world_t *world); /** Signal exit * This operation signals that the application should quit. It will cause * ecs_progress to return false. * * @param world The world to quit. */ FLECS_API void ecs_quit( ecs_world_t *world); /** Deactivate systems that are not matched with tables. * By default Flecs deactivates systems that are not matched with any tables. * However, once a system has been matched with a table it remains activated, to * prevent systems from continuously becoming active and inactive. * * To re-deactivate systems, an application can invoke this function, which will * deactivate all systems that are not matched with any tables. * * @param world The world. */ FLECS_API void ecs_deactivate_systems( ecs_world_t *world); //////////////////////////////////////////////////////////////////////////////// //// Threading //////////////////////////////////////////////////////////////////////////////// /** Set number of worker threads. * Setting this value to a value higher than 1 will start as many threads and * will cause systems to evenly distribute matched entities across threads. The * operation may be called multiple times to reconfigure the number of threads * used, but never while running a system / pipeline. */ FLECS_API void ecs_set_threads( ecs_world_t *world, int32_t threads); /** Get current number of threads. */ FLECS_API int32_t ecs_get_threads( ecs_world_t *world); /** Get current thread index */ FLECS_API int32_t ecs_get_thread_index( ecs_world_t *world); //////////////////////////////////////////////////////////////////////////////// //// Module //////////////////////////////////////////////////////////////////////////////// /* Pipeline component is empty: components and tags in module are static */ typedef struct FlecsPipeline { int32_t dummy; } FlecsPipeline; FLECS_API void FlecsPipelineImport( ecs_world_t *world); #define FlecsPipelineImportHandles(handles) #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_TIMER #ifdef FLECS_STATS #define FLECS_MODULE #define FLECS_PIPELINE #ifndef FLECS_TIMER_H #define FLECS_TIMER_H #ifdef __cplusplus extern "C" { #endif //////////////////////////////////////////////////////////////////////////////// //// Components //////////////////////////////////////////////////////////////////////////////// FLECS_API extern ecs_type_t ecs_type(EcsTimer), ecs_type(EcsRateFilter); /** Component used for timer functionality */ typedef struct EcsTimer { FLECS_FLOAT timeout; /* Timer timeout period */ FLECS_FLOAT time; /* Incrementing time value */ int32_t fired_count; /* Number of times ticked */ bool active; /* Is the timer active or not */ bool single_shot; /* Is this a single shot timer */ } EcsTimer; /* Apply a rate filter to a tick source */ typedef struct EcsRateFilter { ecs_entity_t src; int32_t rate; int32_t tick_count; FLECS_FLOAT time_elapsed; /* Time elapsed since last tick */ } EcsRateFilter; //////////////////////////////////////////////////////////////////////////////// //// Timer API //////////////////////////////////////////////////////////////////////////////// /** Set timer timeout. * This operation executes any systems associated with the timer after the * specified timeout value. If the entity contains an existing timer, the * timeout value will be reset. * * Any entity can be used as a timer (including systems). If a timeout value is * set on a system entity, it will be automatically applied to that system. * * The timer is synchronous, and is incremented each frame by delta_time. * * @param world The world. * @param timer The timer for which to set the timeout (0 to create one). * @param timeout The timeout value. * @return The timer entity. */ FLECS_API ecs_entity_t ecs_set_timeout( ecs_world_t *world, ecs_entity_t timer, FLECS_FLOAT timeout); /** Get current timeout value for the specified timer. * This operation returns the value set by ecs_set_timeout. If no timer is * active for this entity, the operation returns 0. * * After the timeout expires the timer component is removed from the entity. * This means that if ecs_get_timeout is invoked after the timer is expired, the * operation will return 0. * * @param world The world. * @param timer The timer. * @return The current timeout value, or 0 if no timer is active. */ FLECS_API FLECS_FLOAT ecs_get_timeout( ecs_world_t *world, ecs_entity_t timer); /** Set timer interval. * This operation will continously invoke systems associated with the timer * after the interval period expires. If the entity contains an existing timer, * the interval value will be reset. * * Any entity can be used as a timer (including systems). If an interval value * is set on a system entity, it will be automatically applied to that system. * * The timer is synchronous, and is incremented each frame by delta_time. * * @param world The world. * @param timer The timer for which to set the interval (0 to create one). * @param interval The interval value. * @return The timer entity. */ FLECS_API ecs_entity_t ecs_set_interval( ecs_world_t *world, ecs_entity_t timer, FLECS_FLOAT interval); /** Get current interval value for the specified timer. * This operation returns the value set by ecs_set_interval. If no timer is * active for this entity, the operation returns 0. * * @param world The world. * @param timer The timer for which to set the interval. If 0, an entity will be created. * @return The current interval value, or 0 if no timer is active. */ FLECS_API FLECS_FLOAT ecs_get_interval( ecs_world_t *world, ecs_entity_t timer); /** Start timer. * This operation resets the timer and starts it with the specified timeout. The * entity must have the EcsTimer component (added by ecs_set_timeout and * ecs_set_interval). If the entity does not have the EcsTimer component this * operation will assert. * * @param world The world. * @param timer The timer to start. */ FLECS_API void ecs_start_timer( ecs_world_t *world, ecs_entity_t timer); /** Stop timer * This operation stops a timer from triggering. The entity must have the * EcsTimer component or this operation will assert. * * @param world The world. * @param timer The timer to stop. */ FLECS_API void ecs_stop_timer( ecs_world_t *world, ecs_entity_t timer); /** Set rate filter. * This operation sets the source and rate for a rate filter. A rate filter * samples another tick source (or frames, if none provided) and ticks when the * number of sampled ticks equals the rate. * * @param world The world. * @param filter The filter entity (0 to create one). * @param rate The rate to apply. * @param source The tick source (0 to use frames) * @return The filter entity. */ FLECS_API ecs_entity_t ecs_set_rate_filter( ecs_world_t *world, ecs_entity_t filter, int32_t rate, ecs_entity_t source); /** Assign tick source to system. * This operation associates a system with a tick source. If the system is both * active and enabled at the moment the tick source fires, it will be executed. * If no tick source is associated with a system, it will be invoked every * frame. * * To disassociate a tick source from a system, use 0 for the tick_source * parameter. * * Timer and rate filter entities are valid tick sources. An application can * also create its own tick source by setting the EcsTickSource component on an * entity. * * If an entity without the EcsTickSource component is provided as tick source, * the system will not be executed. * * @param world The world. * @param system The system to associate with the timer. * @param timer The timer to associate with the system. */ FLECS_API void ecs_set_tick_source( ecs_world_t *world, ecs_entity_t system, ecs_entity_t tick_source); //////////////////////////////////////////////////////////////////////////////// //// Module //////////////////////////////////////////////////////////////////////////////// /* Timers module component */ typedef struct FlecsTimer { int32_t dummy; } FlecsTimer; FLECS_API void FlecsTimerImport( ecs_world_t *world); #define FlecsTimerImportHandles(handles) #ifdef __cplusplus } #endif #endif #endif #endif /* Optional addons */ #ifdef FLECS_BULK #ifdef FLECS_BULK /** * @file bulk.h * @brief Bulk API. */ #ifndef FLECS_BULK_H #define FLECS_BULK_H #ifdef __cplusplus extern "C" { #endif /** Add an entity to entities matching a filter. * This operation is the same as ecs_add_entity, but is applied to all entities * that match the provided filter. * * @param world The world. * @param entity_add The entity to add. * @param filter The filter. */ FLECS_API void ecs_bulk_add_entity( ecs_world_t *world, ecs_entity_t entity_add, const ecs_filter_t *filter); /** Add a type to entities matching a filter. * This operation is the same as ecs_add_type but is applied to all entities * that match the provided filter. * * @param world The world. * @param type The type to add. * @param filter The filter. */ FLECS_API void ecs_bulk_add_type( ecs_world_t *world, ecs_type_t type, const ecs_filter_t *filter); /** Add a component / type / tag to entities matching a filter. * This operation is the same as ecs_add but is applied to all entities * that match the provided filter. * * @param world The world. * @param type The component, type or tag to add. * @param filter The filter. */ #define ecs_bulk_add(world, type, filter)\ ecs_bulk_add_type(world, ecs_type(type), filter) /** Removes an entity from entities matching a filter. * This operation is the same as ecs_remove_entity, but is applied to all * entities that match the provided filter. * * @param world The world. * @param entity_remove The entity to remove. * @param filter The filter. */ FLECS_API void ecs_bulk_remove_entity( ecs_world_t *world, ecs_entity_t entity_remove, const ecs_filter_t *filter); /** Remove a type from entities matching a filter. * This operation is the same as ecs_remove_type but is applied to all entities * that match the provided filter. * * @param world The world. * @param type The type to remove. * @param filter The filter. */ FLECS_API void ecs_bulk_remove_type( ecs_world_t *world, ecs_type_t type, const ecs_filter_t *filter); /** Add a component / type / tag to entities matching a filter. * This operation is the same as ecs_remove but is applied to all entities * that match the provided filter. * * @param world The world. * @param type The component, type or tag to remove. * @param filter The filter. */ #define ecs_bulk_remove(world, type, filter)\ ecs_bulk_remove_type(world, ecs_type(type), filter) /** Add / remove type from entities matching a filter. * Combination of ecs_bulk_add_type and ecs_bulk_remove_type. * * @param world The world. * @param to_add The type to add. * @param to_remove The type to remove. * @param filter The filter. */ FLECS_API void ecs_bulk_add_remove_type( ecs_world_t *world, ecs_type_t to_add, ecs_type_t to_remove, const ecs_filter_t *filter); /** Add / remove component, type or tag from entities matching a filter. * Combination of ecs_bulk_add and ecs_bulk_remove. * * @param world The world. * @param to_add The component, type or tag to add. * @param to_remove The component, type or tag to remove. * @param filter The filter. */ #define ecs_bulk_add_remove(world, to_add, to_remove, filter)\ ecs_bulk_add_remove_type(world, ecs_type(to_add), ecs_type(to_remove), filter) /** Delete entities matching a filter. * This operation is the same as ecs_delete, but applies to all entities that * match a filter. * * @param world The world. * @param filter The filter. */ FLECS_API void ecs_bulk_delete( ecs_world_t *world, const ecs_filter_t *filter); #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_DBG #ifdef FLECS_DBG #ifndef FLECS_DBG_H #define FLECS_DBG_H #ifdef __cplusplus extern "C" { #endif /* Unstable API */ typedef struct ecs_dbg_entity_t { ecs_entity_t entity; ecs_table_t *table; ecs_type_t type; int32_t row; bool is_watched; } ecs_dbg_entity_t; typedef struct ecs_dbg_table_t { ecs_table_t *table; ecs_type_t type; ecs_type_t shared; ecs_type_t container; ecs_type_t parent_entities; ecs_type_t base_entities; ecs_vector_t *systems_matched; ecs_entity_t *entities; int32_t entities_count; } ecs_dbg_table_t; FLECS_API void ecs_dbg_entity( ecs_world_t *world, ecs_entity_t entity, ecs_dbg_entity_t *dbg_out); FLECS_API ecs_table_t *ecs_dbg_find_table( ecs_world_t *world, ecs_type_t type); FLECS_API ecs_table_t *ecs_dbg_get_table( ecs_world_t *world, int32_t index); FLECS_API bool ecs_dbg_filter_table( ecs_world_t *world, ecs_table_t *table, ecs_filter_t *filter); FLECS_API void ecs_dbg_table( ecs_world_t *world, ecs_table_t *table, ecs_dbg_table_t *dbg_out); #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_MODULE #endif #ifdef FLECS_QUEUE #ifdef FLECS_QUEUE #ifndef FLECS_QUEUE_H_ #define FLECS_QUEUE_H_ #ifdef __cplusplus extern "C" { #endif typedef struct ecs_queue_t ecs_queue_t; FLECS_API ecs_queue_t* _ecs_queue_new( ecs_size_t elem_size, int16_t offset, int32_t elem_count); #define ecs_queue_new(T, elem_count)\ _ecs_queue_new(ECS_VECTOR_T(T), elem_count) FLECS_API ecs_queue_t* _ecs_queue_from_array( ecs_size_t elem_size, int16_t offset, int32_t elem_count, void *array); #define ecs_queue_from_array(T, elem_count, array)\ _ecs_queue_from_array(ECS_VECTOR_T(T), elem_count, array) FLECS_API void* _ecs_queue_push( ecs_queue_t *queue, ecs_size_t elem_size, int16_t offset); #define ecs_queue_push(queue, T)\ (T*)_ecs_queue_push(queue, ECS_VECTOR_T(T)) FLECS_API void* _ecs_queue_get( ecs_queue_t *queue, ecs_size_t elem_size, int16_t offset, int32_t index); #define ecs_queue_get(queue, T, index)\ (T*)_ecs_queue_get(queue, ECS_VECTOR_T(T), index) #define ecs_queue_get_t(vector, size, alignment, index) \ _ecs_queue_get(vector, ECS_VECTOR_U(size, alignment), index) FLECS_API void* _ecs_queue_last( ecs_queue_t *queue, ecs_size_t elem_size, int16_t offset); #define ecs_queue_last(queue, T)\ (T*)_ecs_queue_last(queue, ECS_VECTOR_T(T)) FLECS_API int32_t ecs_queue_index( ecs_queue_t *queue); FLECS_API int32_t ecs_queue_count( ecs_queue_t *queue); FLECS_API void ecs_queue_free( ecs_queue_t *queue); #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_READER_WRITER #ifdef FLECS_READER_WRITER /** * @file serializer.h * @brief Blob serializer API. */ #ifndef FLECS_READER_WRITER_H #define FLECS_READER_WRITER_H #ifdef __cplusplus extern "C" { #endif typedef enum ecs_blob_header_kind_t { EcsStreamHeader, /* Stream states */ EcsTableSegment, EcsFooterSegment, /* Table segment */ EcsTableHeader, EcsTableTypeSize, EcsTableType, EcsTableSize, EcsTableColumn, EcsTableColumnHeader, EcsTableColumnSize, EcsTableColumnData, /* Name column (EcsName) */ EcsTableColumnNameHeader, EcsTableColumnNameLength, EcsTableColumnName, EcsStreamFooter } ecs_blob_header_kind_t; typedef struct ecs_table_reader_t { ecs_blob_header_kind_t state; int32_t table_index; ecs_table_t *table; ecs_data_t *data; /* Current index in type */ ecs_size_t type_written; ecs_type_t type; /* Current column */ ecs_vector_t *column_vector; int32_t column_index; int32_t total_columns; /* Keep track of how much of the component column has been written */ void *column_data; int16_t column_size; int16_t column_alignment; ecs_size_t column_written; /* Keep track of row when writing non-blittable data */ int32_t row_index; int32_t row_count; /* Keep track of how much of an entity name has been written */ const char *name; ecs_size_t name_len; ecs_size_t name_written; bool has_next_table; } ecs_table_reader_t; typedef struct ecs_reader_t { ecs_world_t *world; ecs_blob_header_kind_t state; ecs_iter_t data_iter; ecs_iter_next_action_t data_next; ecs_iter_t component_iter; ecs_iter_next_action_t component_next; ecs_table_reader_t table; } ecs_reader_t; typedef struct ecs_name_writer_t { char *name; int32_t written; int32_t len; int32_t max_len; } ecs_name_writer_t; typedef struct ecs_table_writer_t { ecs_blob_header_kind_t state; ecs_table_t *table; ecs_vector_t *column_vector; /* Keep state for parsing type */ int32_t type_count; int32_t type_max_count; ecs_size_t type_written; ecs_entity_t *type_array; int32_t column_index; int16_t column_size; int16_t column_alignment; ecs_size_t column_written; void *column_data; int32_t row_count; int32_t row_index; ecs_name_writer_t name; } ecs_table_writer_t; typedef struct ecs_writer_t { ecs_world_t *world; ecs_blob_header_kind_t state; ecs_table_writer_t table; int error; } ecs_writer_t; /** Initialize a reader. * A reader serializes data in a world to a sequence of bytes that can be stored * in a file or sent across a network. * * @param world The world to serialize. * @return The reader. */ FLECS_API ecs_reader_t ecs_reader_init( ecs_world_t *world); /** Initialize a snapshot reader. * A snapshot reader serializes data in a snapshot to a sequence of bytes that * can be stored in a file or sent across a network. A snapshot reader has as * advantage that serialization can take place asynchronously while the world * is progressing. * * @param world The world in which the snapshot is taken. * @param iter Iterator to the data to be serialized. * @return The reader. */ FLECS_API ecs_reader_t ecs_reader_init_w_iter( ecs_iter_t *iter, ecs_iter_next_action_t next); /** Read from a reader. * This operation reads a specified number of bytes from a reader and stores it * in the specified buffer. When there are no more bytes to read from the reader * the operation will return 0, otherwise it will return the number of bytes * read. * * The specified buffer must be at least as big as the specified size, and the * specified size must be a multiple of 4. * * @param buffer The buffer in which to store the read bytes. * @param size The maximum number of bytes to read. * @param reader The reader from which to read the bytes. * @return The number of bytes read. */ FLECS_API int32_t ecs_reader_read( char *buffer, int32_t size, ecs_reader_t *reader); /** Initialize a writer. * A writer deserializes data from a sequence of bytes into a world. This * enables applications to restore data from disk or the network. * * The provided world must be either empty or compatible with the data to * deserialize, where compatible means that the serialized component ids and * sizes must match exactly with those in the world. Errors can occur if a world * is provided in which components have been declared in a different order, or * when components have different type definitions. * * @param world The world in which to deserialize the data. * @return The writer. */ FLECS_API ecs_writer_t ecs_writer_init( ecs_world_t *world); /** Write to a writer. * This operation writes a specified number of bytes from a specified buffer * into the writer. The writer will restore the deserialized data into the * original serialized entities. The write operation may be invoked multiple * times with partial buffers, which allows applications to use static buffers * when reading from, for example, a file or the network. * * The data contained in the buffers must have been serialized with the * ecs_reader_read operation. If the data does not match the expected format, or * the data contains conflicts with the world, the operation will fail. The * data must be provided in the same order as produced by ecs_reader_read, * but the used buffer size does not have to be the same as the one used by * ecs_reader_read. The buffer size must be a multiple of 4. * * @param buffer The buffer to deserialize. * @param size The maximum number of bytes. * @param writer The writer to write to. * @return Zero if success, non-zero if failed to deserialize. */ FLECS_API int32_t ecs_writer_write( const char *buffer, int32_t size, ecs_writer_t *writer); #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_SNAPSHOT #ifdef FLECS_SNAPSHOT /** * @file snapshot.h * @brief Snapshot API. */ #ifndef FLECS_SNAPSHOT_H #define FLECS_SNAPSHOT_H #ifdef __cplusplus extern "C" { #endif /** Create a snapshot. * This operation makes a copy of all component in the world that matches the * specified filter. * * @param world The world to snapshot. * @param return The snapshot. */ FLECS_API ecs_snapshot_t* ecs_snapshot_take( ecs_world_t *world); /** Create a filtered snapshot. * This operation is the same as ecs_snapshot_take, but accepts an iterator so * an application can control what is stored by the snapshot. * * @param iter An iterator to the data to be stored by the snapshot. * @param next A function pointer to the next operation for the iterator. * @param return The snapshot. */ FLECS_API ecs_snapshot_t* ecs_snapshot_take_w_iter( ecs_iter_t *iter, ecs_iter_next_action_t action); /** Restore a snapshot. * This operation restores the world to the state it was in when the specified * snapshot was taken. A snapshot can only be used once for restoring, as its * data replaces the data that is currently in the world. * This operation also resets the last issued entity handle, so any calls to * ecs_new may return entity ids that have been issued before restoring the * snapshot. * * The world in which the snapshot is restored must be the same as the world in * which the snapshot is taken. * * @param world The world to restore the snapshot to. * @param snapshot The snapshot to restore. */ FLECS_API void ecs_snapshot_restore( ecs_world_t *world, ecs_snapshot_t *snapshot); /** Obtain iterator to snapshot data. * * @param snapshot The snapshot to iterate over. * @return Iterator to snapshot data. */ FLECS_API ecs_iter_t ecs_snapshot_iter( ecs_snapshot_t *snapshot, const ecs_filter_t *filter); /** Progress snapshot iterator. * * @param iter The snapshot iterator. * @return True if more data is available, otherwise false. */ FLECS_API bool ecs_snapshot_next( ecs_iter_t *iter); /** Free snapshot resources. * This frees resources associated with a snapshot without restoring it. * * @param world The world. * @param snapshot The snapshot to free. */ FLECS_API void ecs_snapshot_free( ecs_snapshot_t *snapshot); #ifdef __cplusplus } #endif #endif #endif #endif /** Direct access API. * * This API allows for low-level direct access to tables and their columns. The * APIs primary intent is to provide fast primitives for new operations. It is * not recommended to use the API directly in application code, as invoking the * API in an incorrect way can lead to a corrupted datastore. */ #ifndef FLECS_DIRECT_ACCESS_H_ #define FLECS_DIRECT_ACCESS_H_ #ifdef __cplusplus extern "C" { #endif /* These functions are part of the core and available even when the direct * access API is not part of a custom build. */ /** Find or create table with specified component string. * The provided string must be a comma-separated list of fully qualified * component identifiers. The returned table will have the specified components. * Two lists that are the same but specify components in a different order will * return the same table. * * @param world The world. * @param type The components. * @return The new or existing table, or NULL if the string contains an error. */ FLECS_API ecs_table_t* ecs_table_from_str( ecs_world_t *world, const char *type); /** Find or create table from type. * Same as ecs_table_from_str, but provides the type directly. * * @param world The world. * @param type The type. * @return The new or existing table. */ FLECS_API ecs_table_t* ecs_table_from_type( ecs_world_t *world, ecs_type_t type); /** Get type for table. * * @param table The table. * @return The type of the table. */ FLECS_API ecs_type_t ecs_table_get_type( ecs_table_t *table); /** Insert record into table. * This will create a new record for the table, which inserts a value for each * component. An optional entity and record can be provided. * * If a non-zero entity id is provided, a record must also be provided and vice * versa. The record must be created by the entity index. If the provided record * is not created for the specified entity, the behavior will be undefined. * * If the provided record is not managed by the entity index, the behavior will * be undefined. * * The returned record contains a reference to the table and the table row. The * data pointed to by the record is guaranteed not to move unless one or more * rows are removed from this table. A row can be removed as result of a delete, * or by adding/removing components from an entity stored in the table. * * @param world The world. * @param table The table. * @param entity The entity. * @param record The entity-index record for the specified entity. * @return A record containing the table and table row. */ FLECS_API ecs_record_t ecs_table_insert( ecs_world_t *world, ecs_table_t *table, ecs_entity_t entity, ecs_record_t *record); /** Returns the number of records in the table. * This operation returns the number of records that have been populated through * the regular (entity) API as well as the number of records that have been * inserted using the direct access API. * * @param world The world. * @param table The table. * @return The number of records in a table. */ FLECS_API int32_t ecs_table_count( ecs_table_t *table); /* From here on functions are only available when the direct access API is part * of a (custom) build. */ #ifdef FLECS_DIRECT_ACCESS /** Find the index of a column in a table. * Table columns are stored in the order of their respective component ids. As * this is not trivial for an application to deduce, this operation returns the * index of a column in a table for a given component. This index can be used * in other table operations to identify a column. * * The returned index is determined separately for each table. Indices obtained * for one table should not be used for another table. * * @param table The table. * @param component The component for which to retrieve the column index. * @return The column index, or -1 if the table does not have the component. */ FLECS_API int32_t ecs_table_find_column( ecs_table_t *table, ecs_entity_t component); /** Get table column. * This operation returns the pointer to a column array. A column contains all * the data for a component for the provided table in a contiguous array. * * The returned pointer is not stable, and may change when a table needs to * resize its arrays, for example in order to accomodate for more records. * * @param table The table. * @param column The column index. * @return Vector that contains the column array. */ FLECS_API ecs_vector_t* ecs_table_get_column( ecs_table_t *table, int32_t column); /** Set table column. * This operation enables an application to set a component column for a table. * After the operation the column is owned by the table. Any operations that * change the column after this operation can cause undefined behavior. * * Care must be taken that all columns in a table have the same number of * elements. If one column has less elements than another, the behavior is * undefined. The operation will not check if the assigned column is of the same * size as other columns, as this would prevent an application from assigning * a set of different columns to a table of a different size. * * Setting a column will not delete the previous column. It is the * responsibility of the application to ensure that the old column is deleted * properly (using ecs_table_delete_column). * * The provided vector must have the same element size and alignment as the * target column. If the size and/or alignment do not match, the behavior will * be undefined. In debug mode the operation may assert. * * If the provided vector is NULL, the table will ensure that a vector is * created for the provided column. If a vector exists that is not of the * same size as the entities vector, it will be resized to match. * * @param world The world. * @param table The table. * @param column The column index. * @param vector The column data to assing. */ FLECS_API ecs_vector_t* ecs_table_set_column( ecs_world_t *world, ecs_table_t *table, int32_t column, ecs_vector_t *vector); /** Get the vector containing entity ids for the table. * This operation obtains the vector with entity ids for the current table. Each * entity id is associated with one record, and ids are stored in the same order * as the table records. The element type of the vector is ecs_entity_t. * * @param table The table. * @return The vector containing the table's entities. */ FLECS_API ecs_vector_t* ecs_table_get_entities( ecs_table_t *table); /** Get the vector containing pointers to entity records. * A table stores cached pointers to entity records for fast access. This * operation provides direct access to the vector. The element type of the * vector is ecs_record_t*. * * @param table The table. * @return The vector containing the entity records. */ FLECS_API ecs_vector_t* ecs_table_get_records( ecs_table_t *table); /** Clear records. * This operation clears records for a world so that they no longer point to a * table. This is useful to ensure that a world is left in a consistent state * after moving data to destination world. * * @param records The vector with record pointers */ FLECS_API void ecs_records_clear( ecs_vector_t *records); /** Initialize records. * This operation ensures entity records are updated to the provided table. * * @param world The world. * @param entities The vector with entity identifiers. * @param records The vector with record pointers. * @param table The table in which the entities are stored. */ FLECS_API void ecs_records_update( ecs_world_t *world, ecs_vector_t *entities, ecs_vector_t *records, ecs_table_t *table); /** Set the vector containing entity ids for the table. * This operation sets the vector with entity ids for a table. In addition the * operation also requires setting a vector with pointers to records. The * record pointers in the vector need to be managed by the entity index. If they * are not, this can cause undefined behavior. * * The provided vectors must have the same number of elements as the number of * records in the table. If the element count is not the same, this causes * undefined behavior. * * A table must have an entity and record vector, even if the table does not * contain entities. For each record that is not an entity, the entity vector * should contain 0, and the record vector should contain NULL. * * @param table The table. * @param entities The entity vector. * @param records The record vector. */ FLECS_API void ecs_table_set_entities( ecs_table_t *table, ecs_vector_t *entities, ecs_vector_t *records); /** Delete a column. * This operation frees the memory of a table column and will invoke the * component destructor if registered. * * The provided vector does not need to be the same as the vector in the table. * The reason the table must be provided is so that the operation can retrieve * the correct destructor for the component. If the component does not have a * destructor, an application can alternatively delete the vector directly. * * If the specified vector is NULL, the column of the table will be removed and * the table will be updated to no longer point at the column. If an explicit * column is provided, the table is not modified. If a column is deleted that is * still being pointed to by a table, behavior is undefined. It is the * responsibility of the application to ensure that a table no longer points to * a deleted column, by using ecs_table_set_column. * * Simultaneously, if this operation is used to delete a table column, the * application should make sure that if the table contains other columns, they * are either also deleted, or that the deleted column is replaced by a column * of the same size. Note that this also goes for the entity and record vectors, * they should have the same number of elements as the other columns. * * The vector must be of the same component as the specified column. If the * vector is not of the same component, behavior will be undefined. In debug * mode the API may assert, though it may not always be able to detect a * mismatching vector/column. * * After this operation the vector should no longer be used by the application. * * @param table The table. * @param column The column index. * @param vector The column vector to delete. */ FLECS_API void ecs_table_delete_column( ecs_world_t *world, ecs_table_t *table, int32_t column, ecs_vector_t *vector); /** Find a record for a given entity. * This operation finds an existing record in the entity index for a given * entity. The returned pointer is stable for the lifecycle of the world and can * be used as argument for the ecs_record_update operation. * * The returned record (if found) points to the adminstration that relates an * entity id to a table. Updating the value of the returned record will cause * operations like ecs_get and ecs_has to look in the updated table. * * Updating this record to a table in which the entity is not stored causes * undefined behavior. * * When the entity has never been created or is not alive this operation will * return NULL. * * @param world The world. * @param entity The entity. * @return The record that belongs to the entity, or NULL if not found. */ FLECS_API ecs_record_t* ecs_record_find( ecs_world_t *world, ecs_entity_t entity); /** Same as ecs_record_find, but creates record if it doesn't exist. * If an entity id has not been created with ecs_new_*, this function can be * used to ensure that a record exists for an entity id. If the provided id * already exists in the world, the operation will return the existing record. * * @param world The world. * @param entity The entity for which to retrieve the record. * @return The (new or existing) record that belongs to the entity. */ FLECS_API ecs_record_t* ecs_record_ensure( ecs_world_t *world, ecs_entity_t entity); /** Get value from record. * This operation gets a component value from a record. The provided column * index must match the table of the record. * * @param r The record. * @param column The column index of the component to get. */ FLECS_API void* ecs_record_get_column( ecs_record_t *r, int32_t column, size_t size); /** Copy value to a component for a record. * This operation sets the component value of a single component for a record. * If the component type has a copy action it will be used, otherwise the value * be memcpyd into the component array. * * The provided record does not need to be managed by the entity index but does * need to point to a valid record in the table. If the provided index is * outside of the range indicating the number of records in the table, behavior * is undefined. In debug mode it will cause the operation to assert. * * @param world The world. * @param r The record to set. * @param column The column index of the component to set. * @param size The size of the component. * @param value Pointer to the value to copy. */ FLECS_API void ecs_record_copy_to( ecs_world_t *world, ecs_record_t *r, int32_t column, size_t size, const void *value, int32_t count); /** Memcpy value to a component for a record. * Same as ecs_record_copy_to, except that this operation will always use * memcpy. This operation should only be used for components that can be safely * memcpyd. If the operation is used for a component that has a copy or move * action, the behavior is undefined. In debug mode the operation may assert. * * @param world The world. * @param r The record to set. * @param column The column index of the component to set. * @param size The size of the component. * @param value Pointer to the value to move. */ FLECS_API void ecs_record_copy_pod_to( ecs_world_t *world, ecs_record_t *r, int32_t column, size_t size, const void *value, int32_t count); /** Move value to a component for a record. * Same as ecs_record_copy_to, except that it uses the move action. If the * component has no move action the value will be memcpyd into the component * array. After this operation the application can no longer assume that the * value passed into the function is valid. * * @param world The world. * @param r The record to set. * @param column The column index of the component to set. * @param size The size of the component. * @param value Pointer to the value to move. */ FLECS_API void ecs_record_move_to( ecs_world_t *world, ecs_record_t *r, int32_t column, size_t size, void *value, int32_t count); #endif #ifdef __cplusplus } #endif #endif #ifdef FLECS_STATS #ifdef FLECS_STATS #define FLECS_SYSTEM #ifndef FLECS_STATS_H #define FLECS_STATS_H #ifdef __cplusplus extern "C" { #endif #define ECS_STAT_WINDOW (60) /** Simple value that indicates current state */ typedef struct ecs_gauge_t { float avg[ECS_STAT_WINDOW]; float min[ECS_STAT_WINDOW]; float max[ECS_STAT_WINDOW]; } ecs_gauge_t; /* Monotonically increasing counter */ typedef struct ecs_counter_t { ecs_gauge_t rate; /**< Keep track of deltas too */ float value[ECS_STAT_WINDOW]; } ecs_counter_t; typedef struct ecs_world_stats_t { /* Allows struct to be initialized with {0} */ int32_t dummy_; ecs_gauge_t entity_count; /**< Number of entities */ ecs_gauge_t component_count; /**< Number of components */ ecs_gauge_t query_count; /**< Number of queries */ ecs_gauge_t system_count; /**< Number of systems */ ecs_gauge_t table_count; /**< Number of tables */ ecs_gauge_t empty_table_count; /**< Number of empty tables */ ecs_gauge_t singleton_table_count; /**< Number of singleton tables. Singleton tables are tables with just a single entity that contains itself */ ecs_gauge_t matched_entity_count; /**< Number of entities matched by queries */ ecs_gauge_t matched_table_count; /**< Number of tables matched by queries */ /* Deferred operations */ ecs_counter_t new_count; ecs_counter_t bulk_new_count; ecs_counter_t delete_count; ecs_counter_t clear_count; ecs_counter_t add_count; ecs_counter_t remove_count; ecs_counter_t set_count; ecs_counter_t discard_count; /* Timing */ ecs_counter_t world_time_total_raw; /**< Actual time passed since simulation start (first time progress() is called) */ ecs_counter_t world_time_total; /**< Simulation time passed since simulation start. Takes into account time scaling */ ecs_counter_t frame_time_total; /**< Time spent processing a frame. Smaller than world_time_total when load is not 100% */ ecs_counter_t system_time_total; /**< Time spent on processing systems. */ ecs_counter_t merge_time_total; /**< Time spent on merging deferred actions. */ ecs_gauge_t fps; /**< Frames per second. */ ecs_gauge_t delta_time; /**< Delta_time. */ /* Frame data */ ecs_counter_t frame_count_total; /**< Number of frames processed. */ ecs_counter_t merge_count_total; /**< Number of merges executed. */ ecs_counter_t pipeline_build_count_total; /**< Number of system pipeline rebuilds (occurs when an inactive system becomes active). */ ecs_counter_t systems_ran_frame; /**< Number of systems ran in the last frame. */ /** Current position in ringbuffer */ int32_t t; } ecs_world_stats_t; /* Statistics for a single query (use ecs_get_query_stats) */ typedef struct ecs_query_stats_t { ecs_gauge_t matched_table_count; /**< Number of matched non-empty tables. This is the number of tables * iterated over when evaluating a query. */ ecs_gauge_t matched_empty_table_count; /**< Number of matched empty tables. Empty tables are not iterated over when * evaluating a query. */ ecs_gauge_t matched_entity_count; /**< Number of matched entities across all tables */ /** Current position in ringbuffer */ int32_t t; } ecs_query_stats_t; /** Statistics for a single system (use ecs_get_system_stats) */ typedef struct ecs_system_stats_t { ecs_query_stats_t query_stats; ecs_counter_t time_spent; /**< Time spent processing a system */ ecs_counter_t invoke_count; /**< Number of times system is invoked */ ecs_gauge_t active; /**< Whether system is active (is matched with >0 entities) */ ecs_gauge_t enabled; /**< Whether system is enabled */ } ecs_system_stats_t; /** Statistics for all systems in a pipeline. */ typedef struct ecs_pipeline_stats_t { /** Vector with system ids of all systems in the pipeline. The systems are * stored in the order they are executed. Merges are represented by a 0. */ ecs_vector_t *systems; /** Map with system statistics. For each system in the systems vector, an * entry in the map exists of type ecs_system_stats_t. */ ecs_map_t *system_stats; } ecs_pipeline_stats_t; /** Get world statistics. * Obtain statistics for the provided world. This operation loops several times * over the tables in the world, and can impact application performance. * * @param world The world. * @param stats Out parameter for statistics. */ FLECS_API void ecs_get_world_stats( ecs_world_t *world, ecs_world_stats_t *stats); /** Print world statistics. * Print statistics obtained by ecs_get_world_statistics and in the * ecs_world_info_t struct. * * @param world The world. * @param stats The statistics to print. */ FLECS_API void ecs_dump_world_stats( ecs_world_t *world, const ecs_world_stats_t *stats); /** Get query statistics. * Obtain statistics for the provided query. * * @param world The world. * @param query The query. * @param stats Out parameter for statistics. */ FLECS_API void ecs_get_query_stats( ecs_world_t *world, ecs_query_t *query, ecs_query_stats_t *s); /** Get system statistics. * Obtain statistics for the provided system. * * @param world The world. * @param system The system. * @param stats Out parameter for statistics. * @return true if success, false if not a system. */ FLECS_API bool ecs_get_system_stats( ecs_world_t *world, ecs_entity_t system, ecs_system_stats_t *stats); /** Get pipeline statistics. * Obtain statistics for the provided pipeline. * * @param world The world. * @param pipeline The pipeline. * @param stats Out parameter for statistics. * @return true if success, false if not a pipeline. */ FLECS_API bool ecs_get_pipeline_stats( ecs_world_t *world, ecs_entity_t pipeline, ecs_pipeline_stats_t *stats); /** Measure frame time. * Frame time measurements measure the total time passed in a single frame, and * how much of that time was spent on systems and on merging. * * Frame time measurements add a small constant-time overhead to an application. * When an application sets a target FPS, frame time measurements are enabled by * default. * * @param world The world. * @param enable Whether to enable or disable frame time measuring. */ FLECS_API void ecs_measure_frame_time( ecs_world_t *world, bool enable); /** Measure system time. * System time measurements measure the time spent in each system. * * System time measurements add overhead to every system invocation and * therefore have a small but measurable impact on application performance. * System time measurements must be enabled before obtaining system statistics. * * @param world The world. * @param enable Whether to enable or disable system time measuring. */ FLECS_API void ecs_measure_system_time( ecs_world_t *world, bool enable); FLECS_API void ecs_gauge_reduce( ecs_gauge_t *dst, int32_t t_dst, ecs_gauge_t *src, int32_t t_src); #ifdef __cplusplus } #endif #endif #endif #endif #ifdef __cplusplus } #ifndef FLECS_NO_CPP #ifndef FLECS_LEGACY #pragma once /* Unstable API */ #include #include #include #include namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// Forward declarations and types //////////////////////////////////////////////////////////////////////////////// using world_t = ecs_world_t; using entity_t = ecs_entity_t; using type_t = ecs_type_t; using snapshot_t = ecs_snapshot_t; using filter_t = ecs_filter_t; using query_t = ecs_query_t; class world; class snapshot; class entity; class type; class iter; class filter; class filter_iterator; class child_iterator; class world_filter; class snapshot_filter; template class query_iterator; template class query; template class system; enum match_kind { MatchAll = EcsMatchAll, MatchAny = EcsMatchAny, MatchExact = EcsMatchExact }; namespace _ { template class component_info; template bool pack_args_to_string( world_t *world, std::stringstream& str, bool is_each = false); } //////////////////////////////////////////////////////////////////////////////// //// Builtin components and tags //////////////////////////////////////////////////////////////////////////////// /* Builtin components */ using Component = EcsComponent; using ComponentLifecycle = EcsComponentLifecycle; using Trigger = EcsTrigger; using Type = EcsType; using Name = EcsName; using Timer = EcsTimer; using RateFilter = EcsRateFilter; using TickSource = EcsTickSource; using SignatureExpr = EcsSignatureExpr; using Signature = EcsSignature; using Query = EcsQuery; using ViewAction = EcsIterAction; using Context = EcsContext; /* Builtin tag ids */ static const ecs_entity_t Module = EcsModule; static const ecs_entity_t Prefab = EcsPrefab; static const ecs_entity_t Hidden = EcsHidden; static const ecs_entity_t Disabled = EcsDisabled; static const ecs_entity_t DisabledIntern = EcsDisabledIntern; static const ecs_entity_t Inactive = EcsInactive; static const ecs_entity_t OnDemand = EcsOnDemand; static const ecs_entity_t Monitor = EcsMonitor; static const ecs_entity_t Pipeline = EcsPipeline; /* Trigger tags */ static const ecs_entity_t OnAdd = EcsOnAdd; static const ecs_entity_t OnRemove = EcsOnRemove; static const ecs_entity_t OnSet = EcsOnSet; /* Builtin pipeline tags */ static const ecs_entity_t PreFrame = EcsPreFrame; static const ecs_entity_t OnLoad = EcsOnLoad; static const ecs_entity_t PostLoad = EcsPostLoad; static const ecs_entity_t PreUpdate = EcsPreUpdate; static const ecs_entity_t OnUpdate = EcsOnUpdate; static const ecs_entity_t OnValidate = EcsOnValidate; static const ecs_entity_t PostUpdate = EcsPostUpdate; static const ecs_entity_t PreStore = EcsPreStore; static const ecs_entity_t OnStore = EcsOnStore; static const ecs_entity_t PostFrame = EcsPostFrame; /** Builtin entity ids */ static const ecs_entity_t World = EcsWorld; static const ecs_entity_t Singleton = EcsSingleton; /** Builtin roles */ static const ecs_entity_t Childof = ECS_CHILDOF; static const ecs_entity_t Instanceof = ECS_INSTANCEOF; static const ecs_entity_t Trait = ECS_TRAIT; static const ecs_entity_t Switch = ECS_SWITCH; static const ecs_entity_t Case = ECS_CASE; static const ecs_entity_t Owned = ECS_OWNED; //////////////////////////////////////////////////////////////////////////////// /** Unsafe wrapper class around a column. * This class can be used when a system does not know the type of a column at * compile time. */ class unsafe_column { public: unsafe_column(void* array, size_t size, size_t count, bool is_shared = false) : m_array(array) , m_size(size) , m_count(count) , m_is_shared(is_shared) {} /** Return element in component array. * This operator may only be used if the column is not shared. * * @param index Index of element. * @return Reference to element. */ void* operator[](size_t index) { ecs_assert(index < m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); ecs_assert(!m_is_shared, ECS_INVALID_PARAMETER, NULL); return ECS_OFFSET(m_array, m_size * index); } /** Return whether component is set. * If the column is optional, this method may return false. * * @return True if component is set, false if component is not set. */ bool is_set() const { return m_array != nullptr; } /** Return whether component is shared. * If the column is shared, this method returns true. * * @return True if component is shared, false if component is owned. */ bool is_shared() const { return m_is_shared; } protected: void* m_array; size_t m_size; size_t m_count; bool m_is_shared; }; /** Wrapper class around a column. * * @tparam T component type of the column. */ template class column { public: /** Create column from component array. * * @param array Pointer to the component array. * @param count Number of elements in component array. * @param is_shared Is the component shared or not. */ column(T* array, size_t count, bool is_shared = false) : m_array(array) , m_count(count) , m_is_shared(is_shared) {} /** Create column from iterator. * * @param iter Iterator object. * @param column Index of the signature of the query being iterated over. */ column(iter &iter, int column); /** Return element in component array. * This operator may only be used if the column is not shared. * * @param index Index of element. * @return Reference to element. */ T& operator[](size_t index) { ecs_assert(index < m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); ecs_assert(!index || !m_is_shared, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); return m_array[index]; } /** Return first element of component array. * This operator is typically used when the column is shared. * * @return Reference to the first element. */ T* operator->() { ecs_assert(m_array != nullptr, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); return m_array; } /** Return whether component is set. * If the column is optional, this method may return false. * * @return True if component is set, false if component is not set. */ bool is_set() const { return m_array != nullptr; } /** Return whether component is shared. * If the column is shared, this method returns true. * * @return True if component is shared, false if component is owned. */ bool is_shared() const { return m_is_shared; } protected: T* m_array; size_t m_count; bool m_is_shared; }; //////////////////////////////////////////////////////////////////////////////// namespace _ { /** Similar to flecs::column, but abstracts away from shared / owned columns. * * @tparam T component type of the column. */ template class any_column { }; template class any_column::value == true>::type > final : public column::type> { public: any_column(T array, std::size_t count, bool is_shared = false) : column::type>(array, count, is_shared) { } T operator[](size_t index) { if (!this->m_is_shared) { ecs_assert(index < this->m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); if (this->m_array) { return &this->m_array[index]; } else { return nullptr; } } else { return &this->m_array[0]; } } }; template class any_column::value == false>::type> final : public column { public: any_column(T* array, std::size_t count, bool is_shared = false) : column(array, count, is_shared) { } T& operator[](size_t index) { if (!this->m_is_shared) { ecs_assert(index < this->m_count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); return this->m_array[index]; } else { return this->m_array[0]; } } }; //////////////////////////////////////////////////////////////////////////////// /** Iterate over an integer range (used to iterate over entity range). * * @tparam Type of the iterator */ template class range_iterator { public: explicit range_iterator(T value) : m_value(value){} bool operator!=(range_iterator const& other) const { return m_value != other.m_value; } T const& operator*() const { return m_value; } range_iterator& operator++() { ++m_value; return *this; } private: T m_value; }; } // namespace _ //////////////////////////////////////////////////////////////////////////////// /** Class that enables iterating over table columns. */ class iter final { using row_iterator = _::range_iterator; public: /** Construct iterator from C iterator object. * This operation is typically not invoked directly by the user. * * @param it Pointer to C iterator. */ iter(const ecs_iter_t *it) : m_iter(it) { m_begin = 0; m_end = static_cast(it->count); } row_iterator begin() const { return row_iterator(m_begin); } row_iterator end() const { return row_iterator(m_end); } /** Obtain handle to current system. */ flecs::entity system() const; /** Obtain current world. */ flecs::world world() const; /** Number of entities to iterate over. */ size_t count() const { return static_cast(m_iter->count); } /** Number of columns in iteator. */ int32_t column_count() const { return m_iter->column_count; } /** Size of column data type. * * @param col The column id. */ size_t column_size(int32_t col) const { return ecs_column_size(m_iter, col); } /** Return delta_time of current frame. */ FLECS_FLOAT delta_time() const { return m_iter->delta_time; } /** Return time elapsed since last time system was invoked. */ FLECS_FLOAT delta_system_time() const { return m_iter->delta_system_time; } /** Return total time passed in simulation. */ FLECS_FLOAT world_time() const { return m_iter->world_time; } /** Returns whether column is shared. * * @param col The column id. */ bool is_shared(int32_t col) const { return !ecs_is_owned(m_iter, col); } /** Returns whether column is owned. * * @param col The column id. */ bool is_owned(int32_t col) const { return ecs_is_owned(m_iter, col); } /** Returns whether column is set. * * @param col The column id. */ bool is_set(int32_t col) const { return ecs_column_w_size(m_iter, 0, col) != NULL; } /** Access param field. * The param field contains the value assigned to flecs::Context, or the * value passed to the `param` argument when invoking system::run. */ void* param() { return m_iter->param; } /** Returns whether column is readonly. * * @param col The column id. */ bool is_readonly(int32_t col) const { return ecs_is_readonly(m_iter, col); } /** Obtain column source (0 if self) * * @param col The column id. */ flecs::entity column_source(int32_t col) const; /** Obtain component/tag entity of column. * * @param col The column id. */ flecs::entity column_entity(int32_t col) const; /** Obtain type of column * * @param col The column id. */ flecs::type column_type(int32_t col) const; /** Obtain entity being iterated over for row. * * @param row Row being iterated over. */ flecs::entity entity(size_t row) const; /** Obtain type of table being iterated over. */ type table_type() const; /** Obtain the total number of tables the iterator will iterate over. */ int32_t table_count() const { return m_iter->table_count; } /** Obtain the total number of inactive tables the query is matched with. */ int32_t inactive_table_count() const { return m_iter->inactive_table_count; } /** Obtain untyped pointer to table column. * * @param table_column Id of table column (corresponds with location in table type). * @return Pointer to table column. */ void* table_column(int32_t col) const { return ecs_table_column(m_iter, col); } /** Obtain typed pointer to table column. * If the table does not contain a column with the specified type, the * function will assert. * * @tparam T Type of the table column. */ template flecs::column table_column() const { auto type = ecs_iter_type(m_iter); auto col = ecs_type_index_of(type, _::component_info::id()); ecs_assert(col != -1, ECS_INVALID_PARAMETER, NULL); return flecs::column(static_cast(ecs_table_column(m_iter, col)), static_cast(m_iter->count), false); } /** Obtain column with const type. * If the specified column id does not match with the provided type, the * function will assert. * * @tparam T Type of the column. * @param col The column id. * @return The component column. */ template ::value, void>::type* = nullptr> flecs::column column(int32_t col) const { return get_column(col); } /** Obtain column with non-const type. * If the specified column id does not match with the provided type or if * the column is readonly, the function will assert. * * @tparam T Type of the column. * @param col The column id. * @return The component column. */ template ::value == false, void>::type* = nullptr> flecs::column column(int32_t col) const { ecs_assert(!ecs_is_readonly(m_iter, col), ECS_COLUMN_ACCESS_VIOLATION, NULL); return get_column(col); } /** Obtain unsafe column. * Unsafe columns are required when a system does not know at compile time * which component will be passed to it. * * @param col The column id. */ flecs::unsafe_column column(int32_t col) const { return get_unsafe_column(col); } /** Obtain owned column. * Same as iter::column, but ensures that column is owned. * * @tparam Type of the column. * @param col The column id. * @return The component column. */ template flecs::column owned(int32_t col) const { ecs_assert(!!ecs_is_owned(m_iter, col), ECS_COLUMN_IS_SHARED, NULL); return this->column(col); } /** Obtain shared column. * Same as iter::column, but ensures that column is shared. * * @tparam Type of the column. * @param col The column id. * @return The component column. */ template const T& shared(int32_t col) const { ecs_assert(ecs_column_entity(m_iter, col) == _::component_info::id(m_iter->world), ECS_COLUMN_TYPE_MISMATCH, NULL); ecs_assert(!ecs_is_owned(m_iter, col), ECS_COLUMN_IS_NOT_SHARED, NULL); return *static_cast(ecs_column_w_size(m_iter, sizeof(T), col)); } /** Obtain single const element of owned or shared column. * If the specified column id does not match with the provided type the * function will assert. * * @tparam Type of the element. * @param col The column id. * @param row The current row. * @return The component element. */ template ::value, void>::type* = nullptr> T& element(int32_t col, int32_t row) const { return get_element(col, row); } /** Obtain single const element of owned or shared column. * If the specified column id does not match with the provided type or if * the column is readonly, the function will assert. * * @tparam Type of the element. * @param col The column id. * @param row The current row. * @return The component element. */ template ::value == false, void>::type* = nullptr> T& element(int32_t col, int32_t row) const { ecs_assert(!ecs_is_readonly(m_iter, col), ECS_COLUMN_ACCESS_VIOLATION, NULL); return get_element(col, row); } private: /* Get column, check if correct type is used */ template flecs::column get_column(int32_t column_id) const { #ifndef NDEBUG ecs_entity_t column_entity = ecs_column_entity(m_iter, column_id); ecs_assert(column_entity & ECS_TRAIT || column_entity & ECS_SWITCH || column_entity & ECS_CASE || column_entity == _::component_info::id(m_iter->world), ECS_COLUMN_TYPE_MISMATCH, NULL); #endif size_t count; bool is_shared = !ecs_is_owned(m_iter, column_id); /* If a shared column is retrieved with 'column', there will only be a * single value. Ensure that the application does not accidentally read * out of bounds. */ if (is_shared) { count = 1; } else { /* If column is owned, there will be as many values as there are * entities. */ count = static_cast(m_iter->count); } return flecs::column( static_cast(ecs_column_w_size(m_iter, sizeof(T), column_id)), count, is_shared); } flecs::unsafe_column get_unsafe_column(int32_t column_id) const { size_t count; size_t size = ecs_column_size(m_iter, column_id); bool is_shared = !ecs_is_owned(m_iter, column_id); /* If a shared column is retrieved with 'column', there will only be a * single value. Ensure that the application does not accidentally read * out of bounds. */ if (is_shared) { count = 1; } else { /* If column is owned, there will be as many values as there are * entities. */ count = static_cast(m_iter->count); } return flecs::unsafe_column(ecs_column_w_size(m_iter, 0, column_id), size, count, is_shared); } /* Get single field, check if correct type is used */ template T& get_element(int32_t col, int32_t row) const { ecs_assert(ecs_column_entity(m_iter, col) == _::component_info::id(m_iter->world), ECS_COLUMN_TYPE_MISMATCH, NULL); return *static_cast(ecs_element_w_size(m_iter, sizeof(T), col, row)); } const ecs_iter_t *m_iter; std::size_t m_begin; std::size_t m_end; }; template inline column::column(iter &iter, int32_t col) { *this = iter.column(col); } //////////////////////////////////////////////////////////////////////////////// /** The world. * The world is the container of all ECS data and systems. If the world is * deleted, all data in the world will be deleted as well. */ class world final { public: /** Create world. */ world() : m_world( ecs_init() ) , m_owned( true ) { init_builtin_components(); } /** Create world with command line arguments. * Currently command line arguments are not interpreted, but they may be * used in the future to configure Flecs parameters. */ world(int argc, char *argv[]) : m_world( ecs_init_w_args(argc, argv) ) , m_owned( true ) { init_builtin_components(); } /** Create world from C world. */ explicit world(world_t *w) : m_world( w ) , m_owned( false ) { } /** Not allowed to copy a world. May only take a reference. */ world(const world& obj) = delete; world(world&& obj) { m_world = obj.m_world; m_owned = obj.m_owned; obj.m_world = nullptr; obj.m_owned = false; } /** Not allowed to copy a world. May only take a reference. */ world& operator=(const world& obj) = delete; world& operator=(world&& obj) { this->~world(); m_world = obj.m_world; m_owned = obj.m_owned; obj.m_world = nullptr; obj.m_owned = false; return *this; } ~world() { if (m_owned && m_world) { ecs_fini(m_world); } } /** Obtain pointer to C world object. */ world_t* c_ptr() const { return m_world; } /** Enable tracing. * * @param level The tracing level. */ static void enable_tracing(int level) { ecs_tracing_enable(level); } /** Progress world, run all systems. * * @param delta_time Custom delta_time. If 0 is provided, Flecs will automatically measure delta_tiem. */ bool progress(FLECS_FLOAT delta_time = 0.0) const { return ecs_progress(m_world, delta_time); } /** Defer operations until end of frame. * When this operation is invoked while iterating, operations inbetween the * defer_begin and defer_end operations are executed at the end of the frame. * * This operation is thread safe. */ bool defer_begin() { return ecs_defer_begin(m_world); } /** End block of operations to defer. * See defer_begin. * * This operation is thread safe. */ bool defer_end() { return ecs_defer_end(m_world); } /** Set number of threads. * This will distribute the load evenly across the configured number of * threads for each system. * * @param threads Number of threads. */ void set_threads(std::int32_t threads) const { ecs_set_threads(m_world, threads); } /** Get number of threads. * * @return Number of configured threads. */ std::int32_t get_threads() const { return ecs_get_threads(m_world); } /** Get index of current thread. * * @return Unique index for current thread. */ std::int32_t get_thread_index() const { return ecs_get_thread_index(m_world); } /** Set target FPS * This will ensure that the main loop (world::progress) does not run faster * than the specified frames per second. * * @param target_fps Target frames per second. */ void set_target_fps(FLECS_FLOAT target_fps) const { ecs_set_target_fps(m_world, target_fps); } /** Get target FPS * * @return Configured frames per second. */ FLECS_FLOAT get_target_fps() const { const ecs_world_info_t *stats = ecs_get_world_info(m_world); return stats->target_fps; } /** Get tick * * @return Monotonically increasing frame count. */ std::int32_t get_tick() const { const ecs_world_info_t *stats = ecs_get_world_info(m_world); return stats->frame_count_total; } /** Set timescale * * @return Monotonically increasing frame count. */ void set_time_scale(FLECS_FLOAT mul) const { ecs_set_time_scale(m_world, mul); } /** Get timescale * * @return Monotonically increasing frame count. */ FLECS_FLOAT get_time_scale() const { const ecs_world_info_t *stats = ecs_get_world_info(m_world); return stats->time_scale; } /** Set world context. * Set a context value that can be accessed by anyone that has a reference * to the world. * * @param ctx The world context. */ void set_context(void* ctx) const { ecs_set_context(m_world, ctx); } /** Get world context. * * @return The configured world context. */ void* get_context() const { return ecs_get_context(m_world); } /** Preallocate memory for number of entities. * This function preallocates memory for the entity index. * * @param entity_count Number of entities to preallocate memory for. */ void dim(std::int32_t entity_count) const { ecs_dim(m_world, entity_count); } /** Preallocate memory for type * This function preallocates memory for the component arrays of the * specified type. * * @param type Type to preallocate memory for. * @param entity_count Number of entities to preallocate memory for. */ void dim_type(type_t t, std::int32_t entity_count) const { ecs_dim_type(m_world, t, entity_count); } /** Set entity range. * This function limits the range of issued entity ids between min and max. * * @param min Minimum entity id issued. * @param max Maximum entity id issued. */ void set_entity_range(entity_t min, entity_t max) const { ecs_set_entity_range(m_world, min, max); } /** Enforce that operations cannot modify entities outside of range. * This function ensures that only entities within the specified range can * be modified. Use this function if specific parts of the code only are * allowed to modify a certain set of entities, as could be the case for * networked applications. * * @param enabled True if range check should be enabled, false if not. */ void enable_range_check(bool enabled) const { ecs_enable_range_check(m_world, enabled); } /** Disables inactive systems. * * This removes systems that are not matched with any entities from the main * loop. Systems are only added to the main loop after they first match with * entities, but are not removed automatically. * * This function allows an application to manually disable inactive systems * which removes them from the main loop. Doing so will cause Flecs to * rebuild the pipeline in the next iteration. * * @param level The tracing level. */ void deactivate_systems() { ecs_deactivate_systems(m_world); } /** Lookup entity by name. * * @param name Entity name. */ flecs::entity lookup(const char *name) const; /** Lookup entity by name. * * @overload */ flecs::entity lookup(std::string& name) const; /** Set singleton component. */ template void set(T value) const; /** Get mut singleton component. */ template T* get_mut() const; /** Mark singleton component as modified. */ template void modified() const; /** Patch singleton component. */ template void patch(std::function func) const; /** Get singleton component. */ template const T* get() const; /** Test if world has singleton component. */ template bool has() const; /** Remove singleton component. */ template void remove() const; /** Get id for type. */ template entity_t type_id() { return _::component_info::id(m_world); } /** Get singleton entity for type. */ template flecs::entity singleton(); /** Create alias for component. * * @tparam Component to create an alias for. * @param alias Alias for the component. */ template flecs::entity use(const char *alias = nullptr); /** Create alias for entity. * * @param name Name of the entity. * @param alias Alias for the entity. */ flecs::entity use(const char *name, const char *alias = nullptr); /** Create alias for entity. * * @param entity Entity for which to create the alias. * @param alias Alias for the entity. */ void use(flecs::entity entity, const char *alias = nullptr); /** Delete all entities matching a filter. * * @param filter The filter to use for matching. */ void delete_entities(flecs::filter filter) const; /** Add component to all entities matching a filter. * * @tparam T The component to add. * @param filter The filter to use for matching. */ template void add(flecs::filter filter) const; /** Add type to all entities. * * @param type The type to add. */ void add(flecs::type type) const; /** Add type to all entities matching a filter. * * @param type The type to add. * @param filter The filter to use for matching. */ void add(flecs::type type, flecs::filter filter) const; /** Add entity to all entities. * * @param entity The entity to add. */ void add(flecs::entity entity) const; /** Add entity to all entities matching a filter. * * @param entity The entity to add. * @param filter The filter to use for matching. */ void add(flecs::entity entity, flecs::filter filter) const; /** Remove component from all entities matching a filter. * * @tparam T The component to remove. * @param filter The filter to use for matching. */ template void remove(flecs::filter filter) const; /** Remove type from all entities. * * @param type The component to remove. */ void remove(flecs::type type) const; /** Remove type from all entities matching a filter. * * @tparam T The component to remove. * @param filter The filter to use for matching. */ void remove(flecs::type type, flecs::filter filter) const; /** Remove entity from all entities. * * @param entity The entity to remove. */ void remove(flecs::entity entity) const; /** Remove entity from all entities matching a filter. * * @param entity The entity to remove. * @param filter The filter to use for matching. */ void remove(flecs::entity entity, flecs::filter filter) const; /** Create iterable filter for entities in world. * * @param filter The filter to create. */ world_filter filter(const flecs::filter& filter) const; flecs::filter_iterator begin() const; flecs::filter_iterator end() const; /** Count entities matching a component. * * @tparam T The component to use for matching. */ template int count() const { return ecs_count_type( m_world, _::component_info::type(m_world)); } /** Count entities matching a filter. * * @param filter The filter to use for matching. */ int count(flecs::filter filter) const; /** Enable locking. * * @param enabled True if locking should be enabled, false if not. */ bool enable_locking(bool enabled) { return ecs_enable_locking(m_world, enabled); } /** Lock world. */ void lock() { ecs_lock(m_world); } /** Unlock world. */ void unlock() { ecs_unlock(m_world); } /** Create a prefab. */ template flecs::entity entity(Args &&... args) const; /** Create an entity. */ template flecs::entity prefab(Args &&... args) const; /** Create a type. */ template flecs::type type(Args &&... args) const; /** Create a module. */ template flecs::entity module(Args &&... args) const; /** Import a module. */ template flecs::entity import(); // Cannot be const because modules accept a non-const world /** Create an system. */ template flecs::system system(Args &&... args) const; /** Create a query. */ template flecs::query query(Args &&... args) const; /** Register a component. */ template flecs::entity component(Args &&... args) const; /** Register a POD component. */ template flecs::entity pod_component(Args &&... args) const; /** Register a relocatable component. */ template flecs::entity relocatable_component(Args &&... args) const; /** Create a snapshot. */ template flecs::snapshot snapshot(Args &&... args) const; private: void init_builtin_components(); world_t *m_world; bool m_owned; }; //////////////////////////////////////////////////////////////////////////////// /** Fluent API for chaining entity operations * This class contains entity operations that can be chained. For example, by * using this class, an entity can be created like this: * * flecs::entity e = flecs::entity(world) * .add() * .add(); */ template class entity_builder { using base_type = const Base; public: /** Add an entity to an entity by id. * This adds a raw entity id (64 bit integer) to the type of the current * entity. * * @param entity The entity id to add. */ base_type& add(entity_t entity) const { static_cast(this)->invoke( [entity](world_t *world, entity_t id) { ecs_add_entity(world, id, entity); }); return *static_cast(this); } /** Add a component to an entity. * To ensure the component is initialized, it should have a constructor. * * @tparam T the component type to add. */ template base_type& add() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_add_entity(world, id, _::component_info::id(world)); }); return *static_cast(this); } /** Add an entity to an entity. * Add an entity to the entity. This is typically used for tagging. * * @param entity The entity to add. */ base_type& add(const entity& entity) const; /** Add a type to an entity by its C pointer. * A type is a vector of component ids. This operation adds all components * in a single operation, and is a more efficient version of doing * individual add operations. * * @param type The C type to add. */ base_type& add(type_t type) const { static_cast(this)->invoke( [type](world_t *world, entity_t id) { ecs_add_type(world, id, type); }); return *static_cast(this); } /** Add a type to an entity. * A type is a vector of component ids. This operation adds all components * in a single operation, and is a more efficient version of doing * individual add operations. * * @param type The type to add. */ base_type& add(type type) const; /** Add a trait. * This operation adds a trait for an entity by entity id. If the trait * is a component, a value of the trait type will be associated with the * entity. If the trait is not a component, a value of the component type * will be associated with the entity. If both the trait and component ids * are regular entities, no values will be associated with the entity. * * @param trait The trait id. * @param entity The entity identifier. */ base_type& add_trait(entity_t trait, entity_t entity) const { static_cast(this)->invoke( [entity, trait](world_t *world, entity_t id) { ecs_add_entity(world, id, ecs_trait(entity, trait)); }); return *static_cast(this); } /** Add a trait. * This operation adds a trait for a component. A value of the trait type * will be associated with the entity. * * @tparam T the trait type. * @tparam C the component type. */ template base_type& add_trait() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_add_entity(world, id, ecs_trait(_::component_info::id(world), _::component_info::id(world))); }); return *static_cast(this); } /** Add a trait. * This operation adds a trait for a component. A value of the trait * type will be associated with the entity. * * @tparam T The trait to add. * @param component The component for which to add the trait. */ template base_type& add_trait(flecs::entity component) const; /** Add a trait tag. * This operation adds a trait tag for a component. A value of the component * type will be associated with the entity. Note that the trait tag passed * into this function should not be a component. * * @tparam C The component type. * @param trait The trait identifier. */ template base_type& add_trait_tag(flecs::entity trait) const; /** Add a trait. * This operation adds a trait for an entity by entity id. If the trait * is a component, a value of the trait type will be associated with the * entity. If the trait is not a component, a value of the component type * will be associated with the entity. If both the trait and component ids * are regular entities, no values will be associated with the entity. * * @param trait The trait to add. * @param entity The tag for which to add the trait. */ base_type& add_trait(flecs::entity trait, flecs::entity entity) const; /** Remove an entity from an entity by id. * * @param entity The entity id to remove. */ base_type& remove(entity_t entity) const { static_cast(this)->invoke( [entity](world_t *world, entity_t id) { ecs_remove_entity(world, id, entity); }); return *static_cast(this); } /** Remove a component from an entity. * * @tparam T the type of the component to remove. */ template base_type& remove() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_remove_entity(world, id, _::component_info::id(world)); }); return *static_cast(this); } /** Remove an entity from an entity. * * @param entity The entity to remove. */ base_type& remove(const entity& entity) const; /** Remove a type from an entity by its C pointer. * A type is a vector of component ids. This operation adds all components * in a single operation, and is a more efficient version of doing * individual add operations. * * @param type the pointer to the type to remove. */ base_type& remove(type_t type) const { static_cast(this)->invoke( [type](world_t *world, entity_t id) { ecs_remove_type(world, id, type); }); return *static_cast(this); } /** Remove a type from an entity. * A type is a vector of component ids. This operation adds all components * in a single operation, and is a more efficient version of doing * individual add operations. * * @param type the type to remove. */ base_type& remove(type type) const; /** Remove a trait. * This operation removes a trait for an entity by entity id. * * @param trait The trait to remove. * @param entity The entity for which to remove the trait. */ base_type& remove_trait(entity_t trait, entity_t entity) const { static_cast(this)->invoke( [entity, trait](world_t *world, entity_t id) { ecs_remove_entity(world, id, ecs_trait(entity, trait)); }); return *static_cast(this); } /** Remove a trait. * This operation removes a trait for a component. * * @tparam T The trait to remove. * @tparam C The component for which to remove the trait. */ template base_type& remove_trait() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_remove_entity(world, id, ecs_trait(_::component_info::id(world), _::component_info::id(world))); }); return *static_cast(this); } /** Remove a trait. * This operation removes a trait tag for a component. The trait should not * be a component. * * @tparam T The trait to remove. * @param component The component for which to remove the trait. */ template base_type& remove_trait(flecs::entity component) const; /** Remove a trait tag. * This operation removes a trait tag for a component. The trait should not * be a component. * * @tparam C The component for which to remove the trait. * @param trait The trait to remove. */ template base_type& remove_trait_tag(flecs::entity trait) const; /** Remove a trait. * This operation removes a trait for an entity. * * @param trait The trait to remove. * @param entity The entity for which to remove the trait. */ base_type& remove_trait(flecs::entity trait, flecs::entity entity) const; /** Add a parent entity to an entity by id. * * @param parent The id of the parent to add. */ base_type& add_childof(entity_t parent) const { static_cast(this)->invoke( [parent](world_t *world, entity_t id) { ecs_add_entity(world, id, ECS_CHILDOF | parent); }); return *static_cast(this); } /** Add a parent entity to an entity. * * @param parent The parent to add. */ base_type& add_childof(const entity& parent) const; /** Remove a parent entity from an entity by id. * * @param parent The id of the parent to remove. */ base_type& remove_childof(entity_t parent) const { static_cast(this)->invoke( [parent](world_t *world, entity_t id) { ecs_remove_entity(world, id, ECS_CHILDOF | parent); }); return *static_cast(this); } /** Remove a parent entity from an entity. * * @param parent The parent to remove. */ base_type& remove_childof(const entity& parent) const; /** Add a base entity to an entity by id. * * @param base The base id to add. */ base_type& add_instanceof(entity_t base) const { static_cast(this)->invoke( [base](world_t *world, entity_t id) { ecs_add_entity(world, id, ECS_INSTANCEOF | base); }); return *static_cast(this); } /** Add a base entity to an entity. * * @param base The base to add. */ base_type& add_instanceof(const entity& base) const; /** Remove a base entity from an entity by id. * * @param base The base id to remove. */ base_type& remove_instanceof(entity_t base) const { static_cast(this)->invoke( [base](world_t *world, entity_t id) { ecs_remove_entity(world, id, ECS_INSTANCEOF | base); }); return *static_cast(this); } /** Remove a base entity from an entity. * * @param base The base to remove. */ base_type& remove_instanceof(const entity& base) const; /** Add owned flag for component (forces ownership when instantiating) * * @param entity The entity for which to add the OWNED flag */ base_type& add_owned(entity_t entity) const { static_cast(this)->invoke( [entity](world_t *world, entity_t id) { ecs_add_entity(world, id, ECS_OWNED | entity); }); return *static_cast(this); } /** Add owned flag for component (forces ownership when instantiating) * * @tparam T The component for which to add the OWNED flag */ template base_type& add_owned() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_add_entity(world, id, ECS_OWNED | _::component_info::id(world)); }); return *static_cast(this); } /** Add owned flag for type entity. * This will ensure that all components in the type are owned for instances * of this entity. * * @param type The type for which to add the OWNED flag */ base_type& add_owned(flecs::type type) const; /** Add a switch to an entity by id. * The switch entity must be a type, that is it must have the EcsType * component. Entities created with flecs::type are valid here. * * @param sw The switch entity id to add. */ base_type& add_switch(entity_t sw) const { static_cast(this)->invoke( [sw](world_t *world, entity_t id) { ecs_add_entity(world, id, ECS_SWITCH | sw); }); return *static_cast(this); } /** Add a switch to an entity. * The switch entity must be a type, that is it must have the EcsType * component. * * @param sw The switch entity to add. */ base_type& add_switch(const entity& sw) const; /** Add a switch to an entity. * Any instance of flecs::type can be used as a switch. * * @param sw The switch to add. */ base_type& add_switch(const type& sw) const; /** Remove a switch from an entity by id. * * @param sw The switch entity id to remove. */ base_type& remove_switch(entity_t sw) const { static_cast(this)->invoke( [sw](world_t *world, entity_t id) { ecs_remove_entity(world, id, ECS_SWITCH | sw); }); return *static_cast(this); } /** Remove a switch from an entity. * * @param sw The switch entity to remove. */ base_type& remove_switch(const entity& sw) const; /** Remove a switch from an entity. * Any instance of flecs::type can be used as a switch. * * @param sw The switch to remove. */ base_type& remove_switch(const type& sw) const; /** Add a switch to an entity by id. * The case must belong to a switch that is already added to the entity. * * @param sw_case The case entity id to add. */ base_type& add_case(entity_t sw_case) const { static_cast(this)->invoke( [sw_case](world_t *world, entity_t id) { ecs_add_entity(world, id, ECS_CASE | sw_case); }); return *static_cast(this); } /** Add a switch to an entity by id. * The case must belong to a switch that is already added to the entity. * * @tparam T The case to add. */ template base_type& add_case() const { return this->add_case(_::component_info::id()); } /** Add a case to an entity. * The case must belong to a switch that is already added to the entity. * * @param sw_case The case entity to add. */ base_type& add_case(const entity& sw_case) const; /** Remove a case from an entity by id. * The case must belong to a switch that is already added to the entity. * * @param sw_case The case entity id to remove. */ base_type& remove_case(entity_t sw_case) const { static_cast(this)->invoke( [sw_case](world_t *world, entity_t id) { ecs_remove_entity(world, id, ECS_CASE | sw_case); }); return *static_cast(this); } /** Remove a switch from an entity by id. * The case must belong to a switch that is already added to the entity. * * @tparam T The case to remove. */ template base_type& remove_case() const { return this->remove_case(_::component_info::id()); } /** Remove a case from an entity. * The case must belong to a switch that is already added to the entity. * * @param sw_case The case entity id to remove. */ base_type& remove_case(const entity& sw_case) const; /** Enable an entity. * Enabled entities are matched with systems and can be searched with * queries. */ base_type& enable() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_enable(world, id, true); }); return *static_cast(this); } /** Disable an entity. * Disabled entities are not matched with systems and cannot be searched * with queries, unless explicitly specified in the query expression. */ base_type& disable() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_enable(world, id, true); }); return *static_cast(this); } /** Enable a component. * This sets the enabled bit for this component. If this is the first time * the component is enabled or disabled, the bitset is added. * * @tparam T The component to enable. */ template base_type& enable() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_enable_component_w_entity(world, id, _::component_info::id(), true); }); return *static_cast(this); } /** Disable a component. * This sets the enabled bit for this component. If this is the first time * the component is enabled or disabled, the bitset is added. * * @tparam T The component to enable. */ template base_type& disable() const { static_cast(this)->invoke( [](world_t *world, entity_t id) { ecs_enable_component_w_entity(world, id, _::component_info::id(), false); }); return *static_cast(this); } /** Enable a component. * See enable. * * @param id The component to enable. */ base_type& enable(flecs::entity_t id) const { static_cast(this)->invoke( [id](world_t *world, entity_t e) { ecs_enable_component_w_entity(world, e, id, true); }); return *static_cast(this); } /** Disable a component. * See disable. * * @param id The component to disable. */ base_type& disable(flecs::entity_t id) const { static_cast(this)->invoke( [id](world_t *world, entity_t e) { ecs_enable_component_w_entity(world, e, id, false); }); return *static_cast(this); } /** Enable a component. * See enable. * * @param entity The component to enable. */ base_type& enable(const flecs::entity& entity) const; /** Disable a component. * See disable. * * @param entity The component to disable. */ base_type& disable(const flecs::entity& entity) const; /** Set a component for an entity. * This operation overwrites the component value. If the entity did not yet * have the component, this operation will add it. * * @tparam T The component to set. * @param value The value to assign to the component. */ template const base_type& set(T&& value) const { static_cast(this)->invoke( [&value](world_t *world, entity_t id) { ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_set_ptr_w_entity( world, id, _::component_info::id(world), sizeof(T), &value); }); return *static_cast(this); } /** Set a component for an entity. * This operation overwrites the component value. If the entity did not yet * have the component, this operation will add it. * * @tparam T The component to set. * @param value The value to assign to the component. */ template const base_type& set(const T& value) const { static_cast(this)->invoke( [&value](world_t *world, entity_t id) { ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_set_ptr_w_entity( world, id, _::component_info::id(world), sizeof(T), &value); }); return *static_cast(this); } /** Set a trait for an entity. * This operation overwrites the trait value. If the entity did not yet * have the trait, this operation will add it. * * @tparam T The trait to set. * @tparam C The component for which to set the trait. * @param value The value to assign to the trait. */ template const base_type& set_trait(const T& value) const { static_cast(this)->invoke( [&value](world_t *world, entity_t id) { ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_set_ptr_w_entity(world, id, ecs_trait(_::component_info::id(world), _::component_info::id(world)), sizeof(T), &value); }); return *static_cast(this); } /** Set a trait tag for a component. * This operation overwrites the trait value. If the entity did not yet * have the trait, this operation will add it. * * This operation should be used for traits that are not components. If a * trait is not a component, it will assume the type of the component it is * assigned to. * * @tparam C The component for which to set the trait. * @param value The value to assign to the trait. */ template const base_type& set_trait_tag(flecs::entity trait, const C& value) const; /** Set a trait for an entity. * This operation overwrites the trait value. If the entity did not yet * have the trait, this operation will add it. * * @tparam T The trait to set. * @param value The value to assign to the trait. * @param entity The entity for which to set the trait. */ template const base_type& set_trait(const T& value, flecs::entity entity) const; /** Patch a component value. * This operation allows an application to partially overwrite a component * value. The operation invokes a function with a reference to the value to * write, and a boolean indicating if the component already existed. * * @tparam T The component to patch. * @param func The function invoked by this operation. */ template const base_type& patch(std::function func) const { static_cast(this)->invoke( [&func](world_t *world, entity_t id) { ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); bool is_added; T *ptr = static_cast(ecs_get_mut_w_entity( world, id, _::component_info::id(world), &is_added)); if (ptr) { func(*ptr, !is_added); ecs_modified_w_entity(world, id, _::component_info::id(world)); } }); return *static_cast(this); } /** Patch a component value. * This operation allows an application to partially overwrite a component * value. The operation invokes a function with a reference to the value to * write. * * @tparam T The component to patch. * @param func The function invoked by this operation. */ template const base_type& patch(std::function func) const { static_cast(this)->invoke( [&func](world_t *world, entity_t id) { ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); bool is_added; T *ptr = static_cast(ecs_get_mut_w_entity( world, id, _::component_info::id(world), &is_added)); if (ptr) { func(*ptr); ecs_modified_w_entity(world, id, _::component_info::id(world)); } }); return *static_cast(this); } }; //////////////////////////////////////////////////////////////////////////////// //// Quick and safe access to a component pointer //////////////////////////////////////////////////////////////////////////////// template class ref { public: ref() : m_world( nullptr ) , m_entity( 0 ) , m_ref() { } ref(world_t *world, entity_t entity) : m_world( world ) , m_entity( entity ) , m_ref() { ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_get_ref_w_entity( m_world, &m_ref, m_entity, _::component_info::id(world)); } const T* operator->() { const T* result = static_cast(ecs_get_ref_w_entity( m_world, &m_ref, m_entity, _::component_info::id(m_world))); ecs_assert(result != NULL, ECS_INVALID_PARAMETER, NULL); return result; } const T* get() { if (m_entity) { ecs_get_ref_w_entity( m_world, &m_ref, m_entity, _::component_info::id(m_world)); } return static_cast(m_ref.ptr); } flecs::entity entity() const; private: world_t *m_world; entity_t m_entity; ecs_ref_t m_ref; }; //////////////////////////////////////////////////////////////////////////////// /** Entity class * This class provides access to entity operations. */ class entity : public entity_builder { public: /** Default constructor. */ explicit entity() : m_world( nullptr ) , m_id( 0 ) { } /** Create entity. * * @param world The world in which to create the entity. */ explicit entity(const world& world) : m_world( world.c_ptr() ) , m_id( ecs_new_w_type(m_world, 0) ) { } /** Create entity. * * @param world Pointer to the world in which to create the entity. */ explicit entity(world_t *world) : m_world( world ) { if (m_world) { m_id = ecs_new_w_type(m_world, 0); } } /** Create a named entity. * Named entities can be looked up with the lookup functions. Entity names * may be scoped, where each element in the name is separated by "::". * For example: "Foo::Bar". If parts of the hierarchy in the scoped name do * not yet exist, they will be automatically created. * * @param world The world in which to create the entity. * @param name The entity name. * @param is_component If true, the entity will be created from the pool of component ids (default = false). */ entity(const world& world, const char *name, bool is_component = false) : m_world( world.c_ptr() ) , m_id( ecs_lookup_path_w_sep(m_world, 0, name, "::", "::") ) { if (!m_id) { if (is_component) { m_id = ecs_new_component_id(m_world); } m_id = ecs_add_path_w_sep( m_world, m_id, 0, name, "::", "::"); } } /** Create a named entity. * Named entities can be looked up with the lookup functions. Entity names * may be scoped, where each element in the name is separated by "::". * For example: "Foo::Bar". If parts of the hierarchy in the scoped name do * not yet exist, they will be automatically created. * * @param world The world in which to create the entity. * @param name The entity name. * @param is_component If true, the entity will be created from the pool of component ids (default = false). */ entity(const world& world, std::string name, bool is_component = false) : m_world( world.c_ptr() ) , m_id( ecs_lookup_path_w_sep(m_world, 0, name.c_str(), "::", "::") ) { if (!m_id) { if (is_component) { m_id = ecs_new_component_id(m_world); } m_id = ecs_add_path_w_sep( m_world, m_id, 0, name.c_str(), "::", "::"); } } /** Wrap an existing entity id. * * @param world The world in which the entity is created. * @param id The entity id. */ entity(const world& world, entity_t id) : m_world( world.c_ptr() ) , m_id(id) { } /** Wrap an existing entity id. * * @param world Pointer to the world in which the entity is created. * @param id The entity id. */ entity(world_t *world, entity_t id) : m_world( world ) , m_id(id) { } /** Equality operator. */ bool operator==(const entity& e) { return this->id() == e.id(); } /** Inequality operator. */ bool operator!=(const entity& e) { return this->id() != e.id(); } explicit operator bool() { return m_id != 0; } /** Entity id 0. * This function is useful when the API must provide an entity object that * belongs to a world, but the entity id is 0. * * @param world The world. */ static flecs::entity null(const world& world) { return flecs::entity(world.c_ptr(), (ecs_entity_t)0); } static flecs::entity null() { return flecs::entity(nullptr, (ecs_entity_t)0); } /** Get entity id. * @return The integer entity id. */ entity_t id() const { return m_id; } /** Get lo entity id. * @return A new entity containing the lower 32 bits of the entity id. */ flecs::entity lo() const { return flecs::entity(m_world, ecs_entity_t_lo(m_id)); } /** Get hi entity id. * @return A new entity containing the higher 32 bits of the entity id. */ flecs::entity hi() const { return flecs::entity(m_world, ecs_entity_t_hi(m_id)); } /** Combine two entity ids. * @return A new entity that combines the provided entity ids in the lower * and higher 32 bits of the entity id. */ static flecs::entity comb(flecs::entity lo, flecs::entity hi) { return flecs::entity(lo.world(), ecs_entity_t_comb(lo.id(), hi.id())); } /** Add role. * Roles are added to entity ids in types to indicate which role they play. * Examples of roles are flecs::Instanceof and flecs::Childof. * * @return A new entity with the specified role set. */ flecs::entity add_role(entity_t role) const { return flecs::entity(m_world, m_id | role); } /** Remove role. * Roles are added to entity ids in types to indicate which role they play. * Examples of roles are flecs::Instanceof and flecs::Childof. * * @return A new entity with any roles removed. */ flecs::entity remove_role() const { return flecs::entity(m_world, m_id & ECS_COMPONENT_MASK); } /** Check if entity has specified role. * Roles are added to entity ids in types to indicate which role they play. * Examples of roles are flecs::Instanceof and flecs::Childof. * * @return True if the entity has the role, false otherwise. */ bool has_role(entity_t role) const { return ((m_id & ECS_ROLE_MASK) == role); } /** Check is entity is alive. * * @return True if the entity is alive, false otherwise. */ bool is_alive() { return ecs_is_alive(m_world, m_id); } /** Return the entity name. * * @return The entity name, or an empty string if the entity has no name. */ std::string name() const { const EcsName *name = static_cast( ecs_get_w_entity(m_world, m_id, ecs_entity(EcsName))); if (name && name->value) { return std::string(name->value); } else { return std::string(); } } /** Return the entity path. * * @return The hierarchical entity path, or an empty string if the entity * has no name. */ std::string path(const char *sep = "::", const char *init_sep = "::") const { char *path = ecs_get_path_w_sep(m_world, 0, m_id, 0, sep, init_sep); if (path) { std::string result = std::string(path); ecs_os_free(path); return result; } else { return std::string(); } } bool enabled() { return !ecs_has_entity(m_world, m_id, flecs::Disabled); } /** Return the world. * * @return The world the entity is stored in. */ flecs::world world() const { return flecs::world(m_world); } /** Return the type. * * @return Returns the entity type. */ flecs::type type() const; /** Return type containing the entity. * * @return A type that contains only this entity. */ flecs::type to_type() const; /** Get component value. * * @tparam T The component to get. * @return Pointer to the component value, nullptr if the entity does not * have the component. */ template const T* get() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_w_entity(m_world, m_id, _::component_info::id(m_world))); } /** Get component value (untyped). * * @param component The component to get. * @return Pointer to the component value, nullptr if the entity does not * have the component. */ const void* get(flecs::entity component) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_get_w_entity(m_world, m_id, component.id()); } /** Get component value (untyped). * * @param component The id of the component to get. * @return Pointer to the component value, nullptr if the entity does not * have the component. */ const void* get(entity_t component_id) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_get_w_entity(m_world, m_id, component_id); } /** Get mutable component value. * This operation returns a mutable pointer to the component. If the entity * did not yet have the component, it will be added. If a base entity had * the component, it will be overridden, and the value of the base component * will be copied to the entity before this function returns. * * @tparam T The component to get. * @param is_added If provided, this parameter will be set to true if the component was added. * @return Pointer to the component value. */ template T* get_mut(bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity( m_world, m_id, _::component_info::id(m_world), is_added)); } /** Get mutable component value (untyped). * This operation returns a mutable pointer to the component. If the entity * did not yet have the component, it will be added. If a base entity had * the component, it will be overridden, and the value of the base component * will be copied to the entity before this function returns. * * @param component The component to get. * @param is_added If provided, this parameter will be set to true if the component was added. * @return Pointer to the component value. */ void* get_mut(flecs::entity component, bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_get_mut_w_entity(m_world, m_id, component.id(), is_added); } /** Get mutable component value (untyped). * This operation returns a mutable pointer to the component. If the entity * did not yet have the component, it will be added. If a base entity had * the component, it will be overridden, and the value of the base component * will be copied to the entity before this function returns. * * @param component The id of the component to get. * @param is_added If provided, this parameter will be set to true if the component was added. * @return Pointer to the component value. */ void* get_mut(entity_t component_id, bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_get_mut_w_entity(m_world, m_id, component_id, is_added); } /** Get trait value. * * @tparam T The trait to get. * @tparam C The component for which to get the trait. * @return Pointer to the trait value, nullptr if the entity does not * have the trait. */ template const T* get_trait() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_w_entity(m_world, m_id, ecs_trait( _::component_info::id(m_world), _::component_info::id(m_world)))); } /** Get trait value. * * @tparam T The trait to get. * @param component The component for which to get the trait. * @return Pointer to the trait value, nullptr if the entity does not * have the trait. */ template const T* get_trait(flecs::entity component) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_w_entity(m_world, m_id, ecs_trait( component.id(), _::component_info::id(m_world)))); } /** Get trait tag value. * The trait passed to this function should not be a component. If a trait * is not a component, the trait assumes the type of the component it is * assigned to. * * @tparam C The component for which to get the trait * @param trait The trait to get. * @return Pointer to the trait value, nullptr if the entity does not * have the trait. */ template const C* get_trait_tag(flecs::entity trait) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_w_entity(m_world, m_id, ecs_trait( _::component_info::id(m_world), trait.id()))); } /** Get trait tag value (untyped). * If a trait is not a component, the trait assumes the type of the * component it is assigned to. * * @param trait The trait to get. * @param component The component for which to get the trait. * @return Pointer to the trait value, nullptr if the entity does not * have the trait. */ const void* get_trait(flecs::entity trait, flecs::entity component) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_get_w_entity(m_world, m_id, ecs_trait( component.id(), trait.id())); } /** Get mutable trait value. * This operation returns a mutable pointer to the trait. If the entity * did not yet have the trait, it will be added. If a base entity had * the trait, it will be overridden, and the value of the base trait * will be copied to the entity before this function returns. * * @tparam T The trait to get. * @tparam C The component for which to get the trait. * @param is_added If provided, this parameter will be set to true if the trait was added. * @return Pointer to the trait value. */ template T* get_trait_mut(bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity( m_world, m_id, ecs_trait( _::component_info::id(m_world), _::component_info::id(m_world)), is_added)); } /** Get mutable trait value. * This operation returns a mutable pointer to the trait. If the entity * did not yet have the trait, it will be added. If a base entity had * the trait, it will be overridden, and the value of the base trait * will be copied to the entity before this function returns. * * @tparam T The trait to get. * @param component The component for which to get the trait. * @param is_added If provided, this parameter will be set to true if the trait was added. * @return Pointer to the trait value. */ template T* get_trait_mut(flecs::entity component, bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity( m_world, m_id, ecs_trait( _::component_info::id(m_world), component.id()), is_added)); } /** Get mutable trait tag value. * This operation returns a mutable pointer to the trait. If the entity * did not yet have the trait, it will be added. If a base entity had * the trait, it will be overridden, and the value of the base trait * will be copied to the entity before this function returns. * * The trait passed to the function should not be a component. * * @tparam C The component for which to get the trait. * @param trait The trait to get. * @param is_added If provided, this parameter will be set to true if the trait was added. * @return Pointer to the trait value. */ template C* get_trait_tag_mut(flecs::entity trait, bool *is_added = nullptr) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity( m_world, m_id, ecs_trait( _::component_info::id(m_world), trait.id()), is_added)); } /** Signal that component was modified. * * @tparam T component that was modified. */ template void modified() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_modified_w_entity(m_world, m_id, _::component_info::id(m_world)); } /** Signal that component was modified. * * @param component component that was modified. */ void modified(flecs::entity component) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_modified_w_entity(m_world, m_id, component.id()); } /** Signal that component was modified. * * @param component id of component that was modified. */ void modified(entity_t component) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_modified_w_entity(m_world, m_id, component); } /** Get reference to component. * A reference allows for quick and safe access to a component value, and is * a faster alternative to repeatedly calling 'get' for the same component. * * @tparam T component for which to get a reference. * @return The reference. */ template ref get_ref() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_assert(_::component_info::size() != 0, ECS_INVALID_PARAMETER, NULL); return ref(m_world, m_id); } /** Get parent from an entity. * This operation retrieves the parent entity that has the specified * component. If no parent with the specified component is found, an entity * with id 0 is returned. If multiple parents have the specified component, * the operation returns the first encountered one. * * @tparam T The component for which to find the parent. * @return The parent entity. */ template flecs::entity get_parent() { return flecs::entity(m_world, ecs_get_parent_w_entity(m_world, m_id, _::component_info::id(m_world))); } flecs::entity get_parent(flecs::entity e) { return flecs::entity(m_world, ecs_get_parent_w_entity(m_world, m_id, e.id())); } /** Clear an entity. * This operation removes all components from an entity without recycling * the entity id. */ void clear() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_clear(m_world, m_id); } /** Delete an entity. * Entities have to be deleted explicitly, and are not deleted when the * flecs::entity object goes out of scope. */ void destruct() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); ecs_delete(m_world, m_id); } /** Lookup an entity by name. * Lookup an entity in the scope of this entity. The provided path may * contain double colons as scope separators, for example: "Foo::Bar". * * @param path The name of the entity to lookup. * @return The found entity, or entity::null if no entity matched. */ entity lookup(const char *path) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); auto id = ecs_lookup_path_w_sep(m_world, m_id, path, "::", "::"); return entity(m_world, id); } /** Check if entity has the provided entity. * * @param entity The entity id to check. * @return True if the entity has the provided entity id, false otherwise. */ bool has(entity_t e) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, e); } /** Check if entity has the provided parent. * * @param parent The parent id to check. * @return True if the entity has the provided parent id, false otherwise. */ bool has_childof(entity_t parent) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, ECS_CHILDOF | parent); } /** Check if entity has the provided base. * * @param base The entity id to check. * @return True if the entity has the provided base id, false otherwise. */ bool has_instanceof(entity_t base) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, ECS_INSTANCEOF | base); } /** Check if entity has the provided type. * * @param entity The type pointer to check. * @return True if the entity has the provided type, false otherwise. */ bool has(type_t type) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_type(m_world, m_id, type); } /** Check if entity has the provided entity. * * @param entity The entity to check. * @return True if the entity has the provided entity, false otherwise. */ bool has(const entity& e) const { return has(e.id()); } /** Check if entity has the provided parent. * * @param parent The entity to check. * @return True if the entity has the provided parent, false otherwise. */ bool has_childof(const entity& parent) const { return has_childof(parent.id()); } /** Check if entity has the provided base. * * @param base The entity to check. * @return True if the entity has the provided base, false otherwise. */ bool has_instanceof(const entity& base) const { return has_instanceof(base.id()); } /** Check if entity has the provided component. * * @tparam T The component to check. * @return True if the entity has the provided component, false otherwise. */ template bool has() const { return has(_::component_info::id(m_world)); } /** Check if entity owns the provided entity id. * An entity id is owned if it is not shared from a base entity. * * @param entity The entity id to check. * @return True if the entity owns the provided entity id, false otherwise. */ bool owns(entity_t e) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_owns_entity(m_world, m_id, e, true); } /** Check if entity owns the provided type. * An type is owned if it is not shared from a base entity. * * @param type The type to check. * @return True if the entity owns the provided type, false otherwise. */ bool owns(type_t type) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_type_owns_type(m_world, ecs_get_type(m_world, m_id), type, true); } /** Check if entity owns the provided entity. * An entity is owned if it is not shared from a base entity. * * @param entity The entity to check. * @return True if the entity owns the provided entity, false otherwise. */ bool owns(const entity& e) const { return owns(e.id()); } /** Check if entity owns the provided component. * An component is owned if it is not shared from a base entity. * * @tparam T The component to check. * @return True if the entity owns the provided component, false otherwise. */ template bool owns() const { return owns(_::component_info::id(m_world)); } /** Check if entity has the provided trait. * * @tparam T The trait to check. * @tparam C The component for which to check the trait. * @return True if the entity has the provided trait, false otherwise. */ template bool has_trait() const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, ecs_trait( _::component_info::id(m_world), _::component_info::id(m_world))); } /** Check if entity has the provided trait. * * @tparam T The trait to check. * @param component The component for which to check the trait. * @return True if the entity has the provided trait, false otherwise. */ template bool has_trait(flecs::entity component) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, ecs_trait( component.id(), _::component_info::id(m_world))); } /** Check if entity has the provided trait tag. * The provided trait tag should not be a component. * * @tparam C The component for which to check the trait tag. * @param trait The trait tag to check. * @return True if the entity has the provided trait tag, false otherwise. */ template bool has_trait_tag(flecs::entity trait) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, ecs_trait( _::component_info::id(m_world), trait.id())); } /** Check if entity has the provided trait. * The provided trait should not be a component. * * @param trait The trait to check. * @param component The component for which to check the trait. * @return True if the entity has the provided trait, false otherwise. */ bool has_trait(flecs::entity trait, flecs::entity e) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, ecs_trait( e.id(), trait.id())); } /** Check if entity has the provided switch. * * @param sw The switch to check. * @return True if the entity has the provided switch, false otherwise. */ bool has_switch(flecs::type sw) const; /** Check if entity has the provided case id. * * @param sw_case The case id to check. * @return True if the entity has the provided case, false otherwise. */ bool has_case(entity_t sw_case) const { ecs_assert(m_world != NULL, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return ecs_has_entity(m_world, m_id, flecs::Case | sw_case); } /** Check if entity has the provided case. * * @param sw_case The case to check. * @return True if the entity has the provided case, false otherwise. */ bool has_case(flecs::entity sw_case) const { return this->has_case(sw_case.id()); } template bool has_case() const { return this->has_case(_::component_info::id(m_world)); } /** Get case for switch. * * @param sw The switch for which to obtain the case. * @return True if the entity has the provided case, false otherwise. */ flecs::entity get_case(flecs::type sw) const; /** Test if component is enabled. * * @tparam T The component to test. * @return True if the component is enabled, false if it has been disabled. */ template bool is_enabled() { return ecs_is_component_enabled_w_entity( m_world, m_id, _::component_info::id(m_world)); } /** Test if component is enabled. * * @param id The component to test. * @return True if the component is enabled, false if it has been disabled. */ bool is_enabled(flecs::entity_t id) { return ecs_is_component_enabled_w_entity( m_world, m_id, id); } /** Test if component is enabled. * * @param entity The component to test. * @return True if the component is enabled, false if it has been disabled. */ bool is_enabled(const flecs::entity& e) { return is_enabled(e.id()); } /** Get current delta time. * Convenience function so system implementations can get delta_time, even * if they are using the .each() function. * * @return Current delta_time. */ FLECS_FLOAT delta_time() const { const ecs_world_info_t *stats = ecs_get_world_info(m_world); return stats->delta_time; } /** Return iterator to entity children. * Enables depth-first iteration over entity children. * * @return Iterator to child entities. */ child_iterator children() const; /** Used by builder class. Do not invoke. */ template void invoke(Func&& action) const { action(m_world, m_id); } protected: world_t *m_world; entity_t m_id; }; /** Prefab class */ class prefab final : public entity { public: explicit prefab(const flecs::world& world, const char *name = nullptr) : entity(world, name) { this->add(flecs::Prefab); } }; //////////////////////////////////////////////////////////////////////////////// //// A collection of component ids used to describe the contents of a table //////////////////////////////////////////////////////////////////////////////// class type final : entity { public: explicit type(const flecs::world& world, const char *name = nullptr, const char *expr = nullptr) : entity(world, ecs_new_type(world.c_ptr(), 0, name, expr)) { sync_from_flecs(); } type(const flecs::world& world, type_t t) : entity( world.c_ptr(), 0 ) , m_type( t ) , m_normalized( t ) { } type(world_t *world, type_t t) : entity( world, 0 ) , m_type( t ) , m_normalized( t ) { } type& add(const type& t) { m_type = ecs_type_add(m_world, m_type, t.id()); m_normalized = ecs_type_merge(m_world, m_normalized, t.c_ptr(), nullptr); sync_from_me(); return *this; } type& add(const entity& e) { m_type = ecs_type_add(m_world, m_type, e.id()); m_normalized = ecs_type_add(m_world, m_normalized, e.id()); sync_from_me(); return *this; } type& add_instanceof(const entity& e) { m_type = ecs_type_add(m_world, m_type, e.id() | ECS_INSTANCEOF); m_normalized = ecs_type_add(m_world, m_normalized, e.id() | ECS_INSTANCEOF); sync_from_me(); return *this; } type& add_childof(const entity& e) { m_type = ecs_type_add(m_world, m_type, e.id() | ECS_CHILDOF); m_normalized = ecs_type_add(m_world, m_normalized, e.id() | ECS_CHILDOF); sync_from_me(); return *this; } template type& add_trait() { m_type = ecs_type_add(m_world, m_type, ecs_trait(_::component_info::id(m_world), _::component_info::id(m_world))); m_normalized = ecs_type_add(m_world, m_normalized, ecs_trait(_::component_info::id(m_world), _::component_info::id(m_world))); sync_from_me(); return *this; } template type& add_trait(flecs::entity component) { m_type = ecs_type_add(m_world, m_type, ecs_trait(component.id(), _::component_info::id(m_world))); m_normalized = ecs_type_add(m_world, m_normalized, ecs_trait(component.id(), _::component_info::id(m_world))); sync_from_me(); return *this; } type& add_trait(flecs::entity trait, flecs::entity component) { m_type = ecs_type_add(m_world, m_type, ecs_trait(component.id(), trait.id())); m_normalized = ecs_type_add(m_world, m_normalized, ecs_trait(component.id(), trait.id())); sync_from_me(); return *this; } template type& add_trait_tag(flecs::entity trait) { m_type = ecs_type_add(m_world, m_type, ecs_trait(_::component_info::id(), trait.id())); m_normalized = ecs_type_add(m_world, m_normalized, ecs_trait(_::component_info::id(), trait.id())); sync_from_me(); return *this; } template type& add() { std::stringstream str; if (!_::pack_args_to_string(m_world, str)) { ecs_abort(ECS_INVALID_PARAMETER, NULL); } std::string expr = str.str(); ecs_type_t t = ecs_type_from_str(m_world, expr.c_str()); m_type = ecs_type_merge(m_world, m_type, t, nullptr); m_normalized = ecs_type_merge(m_world, m_normalized, t, nullptr); sync_from_me(); return *this; } std::string str() const { char *str = ecs_type_str(m_world, m_type); std::string result(str); ecs_os_free(str); return result; } type_t c_ptr() const { return m_type; } // Expose entity id without making the entity class public. entity_t id() const { return m_id; } type_t c_normalized() const { return m_normalized; } void enable() const { ecs_assert(m_id != 0, ECS_INVALID_OPERATION, NULL); ecs_enable(m_world, m_id, true); } void disable() const { ecs_assert(m_id != 0, ECS_INVALID_OPERATION, NULL); ecs_enable(m_world, m_id, false); } flecs::vector vector() { return flecs::vector( (ecs_vector_t*)m_normalized ); } private: void sync_from_me() { ecs_assert(m_id != 0, ECS_INVALID_OPERATION, NULL); EcsType *tc = ecs_get_mut(m_world, m_id, EcsType, NULL); if (tc) { tc->type = m_type; tc->normalized = m_normalized; } } void sync_from_flecs() { ecs_assert(m_id != 0, ECS_INVALID_OPERATION, NULL); EcsType *tc = ecs_get_mut(m_world, m_id, EcsType, NULL); if (tc) { m_type = tc->type; m_normalized = tc->normalized; } } type_t m_type; type_t m_normalized; }; //////////////////////////////////////////////////////////////////////////////// //// Entity range, allows for operating on a range of consecutive entities //////////////////////////////////////////////////////////////////////////////// class entity_range final : public entity_builder { public: entity_range(const world& world, std::int32_t count) : m_world(world.c_ptr()) , m_ids( ecs_bulk_new_w_type(m_world, nullptr, count)) , m_count(count) { } entity_range(const world& world, std::int32_t count, flecs::type type) : m_world(world.c_ptr()) , m_ids( ecs_bulk_new_w_type(m_world, type.c_ptr(), count)) , m_count(count) { } template void invoke(Func&& action) const { for (int i = 0; i < m_count; i ++) { action(m_world, m_ids[i]); } } private: world_t *m_world; const entity_t *m_ids; std::int32_t m_count; }; //////////////////////////////////////////////////////////////////////////////// //// Register component, provide global access to component handles / metadata //////////////////////////////////////////////////////////////////////////////// namespace _ { // Trick to obtain typename from type, as described here // https://blog.molecular-matters.com/2015/12/11/getting-the-type-of-a-template-argument-as-string-without-rtti/ // // The code from the link has been modified to work with more types, and across // multiple compilers. // struct name_util { /* Remove parts from typename that aren't needed for component name */ static void trim_name(char *typeName) { ecs_size_t len = ecs_os_strlen(typeName); /* Remove 'const' */ ecs_size_t const_len = ecs_os_strlen("const "); if ((len > const_len) && !ecs_os_strncmp(typeName, "const ", const_len)) { ecs_os_memmove(typeName, typeName + const_len, len - const_len); typeName[len - const_len] = '\0'; len -= const_len; } /* Remove 'struct' */ ecs_size_t struct_len = ecs_os_strlen("struct "); if ((len > struct_len) && !ecs_os_strncmp(typeName, "struct ", struct_len)) { ecs_os_memmove(typeName, typeName + struct_len, len - struct_len); typeName[len - struct_len] = '\0'; len -= struct_len; } /* Remove 'class' */ ecs_size_t class_len = ecs_os_strlen("class "); if ((len > class_len) && !ecs_os_strncmp(typeName, "class ", class_len)) { ecs_os_memmove(typeName, typeName + class_len, len - class_len); typeName[len - class_len] = '\0'; len -= class_len; } while (typeName[len - 1] == ' ' || typeName[len - 1] == '&' || typeName[len - 1] == '*') { len --; typeName[len] = '\0'; } /* Remove const at end of string */ if (len > const_len) { if (!ecs_os_strncmp(&typeName[len - const_len], " const", const_len)) { typeName[len - const_len] = '\0'; } } } }; // Compiler-specific conversion from __PRETTY_FUNCTION__ to component name. // This code uses a trick that instantiates a function for the component type. // Then __PRETTY_FUNCTION__ is used to obtain the name of the function. Because // the result of __PRETTY_FUNCTION__ is not standardized, there are different // implementations for clang, gcc and msvc. Code that uses a different compiler // needs to register component names explicitly. #if defined(__clang__) static const unsigned int FRONT_SIZE = sizeof("static const char* flecs::_::name_helper<") - 1u; static const unsigned int BACK_SIZE = sizeof(">::name() [T = ]") - 1u; template struct name_helper { static const char* name(void) { static const size_t size = (sizeof(__PRETTY_FUNCTION__) - FRONT_SIZE - BACK_SIZE) / 2 + 1u; static char typeName[size + 6] = {}; memcpy(typeName, __PRETTY_FUNCTION__ + FRONT_SIZE, size - 1u); name_util::trim_name(typeName); return typeName; } }; #elif defined(__GNUC__) static const unsigned int FRONT_SIZE = sizeof("static const char* flecs::_::name_helper::name() [with T = ") - 1u; static const unsigned int BACK_SIZE = sizeof("]") - 1u; template struct name_helper { static const char* name(void) { static const size_t size = sizeof(__PRETTY_FUNCTION__) - FRONT_SIZE - BACK_SIZE; static char typeName[size + 6] = {}; memcpy(typeName, __PRETTY_FUNCTION__ + FRONT_SIZE, size - 1u); name_util::trim_name(typeName); return typeName; } }; #elif defined(_WIN32) static const unsigned int FRONT_SIZE = sizeof("flecs::_::name_helper<") - 1u; static const unsigned int BACK_SIZE = sizeof(">::name") - 1u; template struct name_helper { static const char* name(void) { static const size_t size = sizeof(__FUNCTION__) - FRONT_SIZE - BACK_SIZE; static char typeName[size + 6] = {}; memcpy(typeName, __FUNCTION__ + FRONT_SIZE, size - 1u); name_util::trim_name(typeName); return typeName; } }; #elif #error "implicit component registration not supported" #endif // The following functions are lifecycle callbacks that are automatically // registered with flecs to ensure component lifecycle is handled correctly. Not // all types require this, yet callbacks are registered by default, which // introduces some overhead when working with components. // // An application can optimize this by explicitly registering a component as a // plain old datatype, with world.pod_component(). template void component_ctor( ecs_world_t *world, ecs_entity_t component, const ecs_entity_t *entity_ptr, void *ptr, size_t size, int32_t count, void *ctx) { (void)world; (void)component; (void)entity_ptr; (void)size; (void)ctx; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *t_ptr = static_cast(ptr); for (int i = 0; i < count; i ++) { new(&t_ptr[i]) T; } } template void component_dtor( ecs_world_t *world, ecs_entity_t component, const ecs_entity_t *entity_ptr, void *ptr, size_t size, int32_t count, void *ctx) { (void)world; (void)component; (void)entity_ptr; (void)size; (void)ctx; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *t_ptr = static_cast(ptr); for (int i = 0; i < count; i ++) { t_ptr[i].~T(); } } template void component_copy( ecs_world_t *world, ecs_entity_t component, const ecs_entity_t *dst_entity, const ecs_entity_t *src_entity, void *dst_ptr, const void *src_ptr, size_t size, int32_t count, void *ctx) { (void)world; (void)component; (void)dst_entity; (void)src_entity; (void)size; (void)ctx; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *t_dst_ptr = static_cast(dst_ptr); const T *t_src_ptr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { t_dst_ptr[i] = t_src_ptr[i]; } } template void component_move( ecs_world_t *world, ecs_entity_t component, const ecs_entity_t *dst_entity, const ecs_entity_t *src_entity, void *dst_ptr, void *src_ptr, size_t size, int32_t count, void *ctx) { (void)world; (void)component; (void)dst_entity; (void)src_entity; (void)size; (void)ctx; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *t_dst_ptr = static_cast(dst_ptr); T *t_src_ptr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { t_dst_ptr[i] = std::move(t_src_ptr[i]); } } // Register component lifecycle callbacks with flecs. template void register_lifecycle_actions( ecs_world_t *world, ecs_entity_t component, bool ctor, bool dtor, bool copy, bool move) { if (!ecs_component_has_actions(world, component)) { EcsComponentLifecycle cl{}; if (ctor) { cl.ctor = _::component_ctor< typename std::remove_const< typename std::remove_pointer::type>::type>; } if (dtor) { cl.dtor = _::component_dtor< typename std::remove_const< typename std::remove_pointer::type>::type>; } if (copy) { cl.copy = _::component_copy< typename std::remove_const< typename std::remove_pointer::type>::type>; } if (move) { cl.move = _::component_move< typename std::remove_const< typename std::remove_pointer::type>::type>; } ecs_set_component_actions_w_entity( world, component, &cl); } } // Class that holds component identifier and name. // This class is used by most component operations to obtain the component id in // a transparent way for the application. The component id is stored in a static // member of a class, and is shared by all worlds in an application. As such, // each world in an application must (and will) have the same id. // // Because of how global (templated) variables are instantiated, it is possible // that different instances for the same component exist across different // translation units. This is handled transparently by flecs. When a component // id is requested from the component_info class, but the id is uninitialized, a // lookup by name will be performed for the component on the world, which will // return the id with which the component was already registered. This means // component identifiers are eventually consistent across translation units. // // When a component id is requested for a world that had not yet registered the // component, it will be registered with the existing identifier. This ensures // that multiple worlds in the same application use the same component ids. // // There are a few limitations of this approach. // // 1) When two worlds register components in different orders, it is possible // that different components receive the same identifier. When a world // attempts to re-register a component with a different identifier, an error // will be thrown. To prevent this from happening, worlds should register // components in the same order. // // 2) Component names are used to ensure that different translation units use // the same identifiers. If a component is registered under different names // in the same application, id conflicts can occur. // // Known issues: // // It seems like component registration does not always work correctly in Unreal // engine when recreating a world. A plausible cause for this is the hot // reloading of dynamic libraries by the engine. A workaround for this issue is // to call flecs::_::component_info::reset() before recreating the world. // This will reset the global component state and avoids conflicts. The exact // cause of the issue is investigated here: // https://github.com/SanderMertens/flecs/issues/293 template class component_info final { public: // Initialize component identifier static void init(world_t* world, entity_t entity, bool allow_tag = true) { // If an identifier was already set, check for consistency if (s_id) { // If an identifier was registered, a name should've been registered // as well. ecs_assert(s_name.c_str() != nullptr, ECS_INTERNAL_ERROR, NULL); // A component cannot be registered using a different identifier. ecs_assert(s_id == entity, ECS_INCONSISTENT_COMPONENT_ID, _::name_helper::name()); // Ensure the entity has the same name as what was registered. if (s_id >= EcsFirstUserComponentId) { char *path = ecs_get_fullpath(world, entity); ecs_assert(!strcmp(path, s_name.c_str()), ECS_INCONSISTENT_COMPONENT_NAME, _::name_helper::name()); ecs_os_free(path); } // Component was already registered and data is consistent with new // identifier, so nothing else to be done. return; } // Component wasn't registered yet, set the values. Register component // name as the fully qualified flecs path. char *path = ecs_get_fullpath(world, entity); s_id = entity; s_name = path; s_allow_tag = allow_tag; // s_name is an std::string, so it will have made a copy ecs_os_free(path); } // Obtain a component identifier without registering lifecycle callbacks. static entity_t id_no_lifecycle(world_t *world = nullptr, const char *name = nullptr, bool allow_tag = true) { // If no id has been registered yet, do it now. if (!s_id) { if (!name) { // If no name was provided, retrieve the name implicitly from // the name_helper class. name = _::name_helper::name(); } s_allow_tag = allow_tag; // If no world was provided, we can't implicitly register the // component. While there are a few cases where calling this // function without a world is OK, in general functions should // always provide a world to enable implicit registration. ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); // Create entity with name first. This ensurs that the component is // created in the right location in the hierarchy, in case a scoped // name is used. This allows registering components with the same // name in different namespaces. // // If the component was already registered for this world, this will // resolve the existing component identifier. This enables // transparent component registeration across multiple translation // units, as long as the same world is used. // // The last parameter ('true') ensures that when the component was // not yet registered, a new component identifier is created. // Component identifiers and entity identifiers are equivalent, // though a pool of identifiers at the start of the id range is // reserved for components. This is a performance optimization, as // low ids in some parts of the code allow for direct indexing. flecs::world w(world); flecs::entity result = entity(w, name, true); // Now use the resulting identifier to register the component. Note // that the name is not passed into this function, as the entity was // already created with the correct name. ecs_entity_t entity = ecs_new_component( world, result.id(), nullptr, size(), alignment()); // The identifier returned by the function should be the same as the // identifier that was passed in. ecs_assert(entity == result.id(), ECS_INTERNAL_ERROR, NULL); // Init the component_info instance with the identiifer. init(world, entity); } // By now we should have a valid identifier ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); return s_id; } // Obtain a component identifier, register lifecycle callbacks if this is // the first time the component is used. static entity_t id(world_t *world = nullptr, const char *name = nullptr, bool allow_tag = true) { // If no id has been registered yet, do it now. if (!s_id) { // This will register a component id, but will not register // lifecycle callbacks. id_no_lifecycle(world, name, allow_tag); // Register lifecycle callbacks, but only if the component has a // size. Components that don't have a size are tags, and tags don't // require construction/destruction/copy/move's. */ if (size()) { register_lifecycle_actions(world, s_id, true, true, true, true); } } // By now we should have a valid identifier ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); return s_id; } // Obtain a component name static const char* name(world_t *world = nullptr) { // If no id has been registered yet, do it now. if (!s_id) { id(world); } // By now we should have a valid identifier ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); // If the id is set, the name should also have been set return s_name.c_str(); } // Obtain a component name, don't register lifecycle if the component hadn't // been registered yet. While functionally the same could be achieved by // first calling id_no_lifecycle() and then name(), this function ensures // that the lifecycle callback templates are not instantiated. This allows // some types (such as module classes) to be created without a default // constructor. static const char* name_no_lifecycle(world_t *world = nullptr) { // If no id has been registered yet, do it now. if (!s_id) { id_no_lifecycle(world); } // By now we should have a valid identifier ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); // Return return s_name.c_str(); } // Return the type of a component. // The type is a vector of component ids. This will return a type with just // the current component id. static type_t type(world_t *world = nullptr) { // If no id has been registered yet, do it now. if (!s_id) { id(world); } // By now we should have a valid identifier ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); // Create a type from the component id. if (!s_type) { s_type = ecs_type_from_entity(world, s_id); } ecs_assert(s_type != nullptr, ECS_INTERNAL_ERROR, NULL); return s_type; } // Return the size of a component. static size_t size() { // C++ types that have no members still have a size. Use std::is_empty // to check if the type is empty. If so, use 0 for the component size. // // If s_allow_tag is set to false, the size returned by C++ is used. // This is useful in cases where class instances are still required, as // is the case with module classes. if (s_allow_tag && std::is_empty::value) { return 0; } else { return sizeof(typename std::remove_pointer::type); } } // Return the alignment of a component. static size_t alignment() { if (size() == 0) { return 0; } else { return alignof(typename std::remove_pointer::type); } } // Was the component already registered. static bool registered() { return s_id != 0; } // This function is only used to test cross-translation unit features. No // code other than test cases should invoke this function. static void reset() { s_id = 0; s_type = NULL; s_name.clear(); } private: static entity_t s_id; static type_t s_type; static std::string s_name; static bool s_allow_tag; }; // Global templated variables that hold component identifier and other info template entity_t component_info::s_id( 0 ); template type_t component_info::s_type( nullptr ); template std::string component_info::s_name(""); template bool component_info::s_allow_tag( true ); //////////////////////////////////////////////////////////////////////////////// //// Register a component with flecs //////////////////////////////////////////////////////////////////////////////// } // namespace _ /** Plain old datatype, no lifecycle actions are registered */ template flecs::entity pod_component(const flecs::world& world, const char *name = nullptr, bool allow_tag = true) { if (!name) { name = _::name_helper::name(); } world_t *world_ptr = world.c_ptr(); entity_t id = 0; if (_::component_info::registered()) { /* To support components across multiple worlds, ensure that the * component ids are the same. */ id = _::component_info::id_no_lifecycle(world_ptr, name, allow_tag); /* If entity is not empty check if the name matches */ if (ecs_get_type(world_ptr, id) != nullptr) { if (id >= EcsFirstUserComponentId) { char *path = ecs_get_fullpath(world_ptr, id); ecs_assert(!strcmp(path, name), ECS_INCONSISTENT_COMPONENT_NAME, name); ecs_os_free(path); } } /* Register name with entity, so that when the entity is created the * correct id will be resolved from the name. */ ecs_add_path_w_sep(world_ptr, id, 0, name, "::", "::"); /* If a component was already registered with this id but with a * different size, the ecs_new_component function will fail. */ /* We need to explicitly call ecs_new_component here again. Even though * the component was already registered, it may have been registered * with a different world. This ensures that the component is registered * with the same id for the current world. * If the component was registered already, nothing will change. */ ecs_entity_t entity = ecs_new_component( world.c_ptr(), id, nullptr, _::component_info::size(), _::component_info::alignment()); (void)entity; ecs_assert(entity == id, ECS_INTERNAL_ERROR, NULL); /* This functionality could have been put in id_no_lifecycle, but since * this code happens when a component is registered, and the entire API * calls id_no_lifecycle, this would add a lot of overhead to each call. * This is why when using multiple worlds, components should be * registered explicitly. */ } else { /* If the component is not yet registered, ensure no other component * or entity has been registered with this name */ ecs_entity_t entity = ecs_lookup_fullpath(world_ptr, name); (void)entity; ecs_assert(entity == 0, ECS_COMPONENT_NAME_IN_USE, name); id = _::component_info::id_no_lifecycle(world_ptr, name, allow_tag); } _::component_info::init(world_ptr, id, allow_tag); _::component_info::init(world_ptr, id, allow_tag); _::component_info::init(world_ptr, id, allow_tag); _::component_info::init(world_ptr, id, allow_tag); return world.entity(id); } /** Regular component with ctor, dtor copy and move actions */ template flecs::entity component(const flecs::world& world, const char *name = nullptr) { flecs::entity result = pod_component(world, name); if (_::component_info::size()) { _::register_lifecycle_actions(world.c_ptr(), result.id(), true, true, true, true); } return result; } /** Trivially relocatable component that can be memcpy'd. */ template flecs::entity relocatable_component(const flecs::world& world, const char *name = nullptr) { flecs::entity result = pod_component(world, name); _::register_lifecycle_actions(world.c_ptr(), result.id(), true, true, true, false); return result; } template flecs::entity_t type_id() { return _::component_info::id(); } //////////////////////////////////////////////////////////////////////////////// //// Define a module //////////////////////////////////////////////////////////////////////////////// template flecs::entity module(const flecs::world& world, const char *name = nullptr) { ecs_set_scope(world.c_ptr(), 0); flecs::entity result = pod_component(world, name, false); ecs_set_scope(world.c_ptr(), result.id()); return result; } //////////////////////////////////////////////////////////////////////////////// //// Import a module //////////////////////////////////////////////////////////////////////////////// template flecs::entity import(world& world) { if (!_::component_info::registered()) { ecs_trace_1("import %s", _::name_helper::name()); ecs_log_push(); ecs_entity_t scope = ecs_get_scope(world.c_ptr()); // Allocate module, so the this ptr will remain stable T *module_data = new T(world); ecs_set_scope(world.c_ptr(), scope); flecs::entity m = world.lookup(_::component_info::name_no_lifecycle(world.c_ptr())); ecs_set_ptr_w_entity( world.c_ptr(), m.id(), _::component_info::id_no_lifecycle(world.c_ptr()), _::component_info::size(), module_data); ecs_log_pop(); return m; } else { return flecs::entity(world, _::component_info::id_no_lifecycle(world.c_ptr())); } } //////////////////////////////////////////////////////////////////////////////// //// A filter is used to match subsets of tables //////////////////////////////////////////////////////////////////////////////// class filter { public: filter() : m_world( nullptr ) , m_filter{ } {} explicit filter(const world& world) : m_world( world.c_ptr() ) , m_filter{ } { } filter& include(type type) { m_filter.include = ecs_type_merge(m_world, m_filter.include, type.c_ptr(), nullptr); return *this; } filter& include(entity entity) { m_filter.include = ecs_type_add(m_world, m_filter.include, entity.id()); return *this; } template filter& include() { m_filter.include = ecs_type_add(m_world, m_filter.include, _::component_info::id(m_world)); return *this; } filter& include_kind(match_kind kind) { m_filter.include_kind = static_cast(kind); return *this; } type include() { return type(m_world, m_filter.include); } filter& exclude(type type) { m_filter.exclude = ecs_type_merge(m_world, m_filter.exclude, type.c_ptr(), nullptr); return *this; } filter& exclude(entity entity) { m_filter.exclude = ecs_type_add(m_world, m_filter.exclude, entity.id()); return *this; } template filter& exclude() { m_filter.exclude = ecs_type_add(m_world, m_filter.exclude, _::component_info::id(m_world)); return *this; } filter& exclude_kind(match_kind kind) { m_filter.exclude_kind = static_cast(kind); return *this; } type exclude() { return type(m_world, m_filter.exclude); } const filter_t* c_ptr() const { if (m_world) { return &m_filter; } else { return nullptr; } } private: world_t *m_world; filter_t m_filter; }; //////////////////////////////////////////////////////////////////////////////// //// Utility to convert template argument pack to array of columns //////////////////////////////////////////////////////////////////////////////// namespace _ { template class column_args { public: struct Column { void *ptr; bool is_shared; }; using Columns = std::array; column_args(ecs_iter_t* iter) { populate_columns(iter, 0, (typename std::remove_reference::type>::type*)nullptr...); } Columns m_columns; private: /* Dummy function when last component has been added */ void populate_columns(ecs_iter_t *iter, size_t index) { (void)iter; (void)index; } /* Populate columns array recursively */ template void populate_columns(ecs_iter_t *iter, size_t index, T comp, Targs... comps) { int32_t column = static_cast(index + 1); void *ptr = ecs_column_w_size(iter, sizeof(*comp), column); m_columns[index].ptr = ptr; m_columns[index].is_shared = !ecs_is_owned(iter, column) && ptr != nullptr; populate_columns(iter, index + 1, comps ...); } }; //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system each //////////////////////////////////////////////////////////////////////////////// template class each_invoker { using Columns = typename column_args::Columns; public: explicit each_invoker(Func func) : m_func(func) { } // Invoke system template ::type* = nullptr> static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { flecs::iter iter_wrapper(iter); (void)index; (void)columns; // Use any_column so we can transparently use shared components for (auto row : iter_wrapper) { func(iter_wrapper.entity(row), (_::any_column::type>( (typename std::remove_reference< typename std::remove_pointer::type >::type*)comps.ptr, static_cast(iter->count), comps.is_shared))[row]...); } } // Add components one by one to parameter pack template ::type* = nullptr> static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { call_system(iter, func, index + 1, columns, comps..., columns[index]); } // Callback provided to flecs system static void run(ecs_iter_t *iter) { const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); each_invoker *self = (each_invoker*)ctx->ctx; Func func = self->m_func; column_args columns(iter); call_system(iter, func, 0, columns.m_columns); } private: Func m_func; }; //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system action //////////////////////////////////////////////////////////////////////////////// template class action_invoker { using Columns = typename column_args::Columns; public: explicit action_invoker(Func func) : m_func(func) { } /* Invoke system */ template ::type* = nullptr> static void call_system(ecs_iter_t *iter, Func func, int index, Columns& columns, Targs... comps) { (void)index; (void)columns; flecs::iter iter_wrapper(iter); func(iter_wrapper, (column::type >::type>( (typename std::remove_reference< typename std::remove_pointer::type >::type*)comps.ptr, iter->count, comps.is_shared))...); } /** Add components one by one to parameter pack */ template ::type* = nullptr> static void call_system(ecs_iter_t *iter, Func func, int index, Columns& columns, Targs... comps) { call_system(iter, func, index + 1, columns, comps..., columns[index]); } /** Callback provided to flecs */ static void run(ecs_iter_t *iter) { const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); action_invoker *self = (action_invoker*)ctx->ctx; Func func = self->m_func; column_args columns(iter); call_system(iter, func, 0, columns.m_columns); } private: Func m_func; }; //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system iterate action //////////////////////////////////////////////////////////////////////////////// template class iter_invoker { using Columns = typename column_args::Columns; public: explicit iter_invoker(Func func) : m_func(func) { } /* Invoke system */ template ::type* = nullptr> static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { (void)index; (void)columns; flecs::iter iter_wrapper(iter); func(iter_wrapper, ((typename std::remove_reference< typename std::remove_pointer::type >::type*)comps.ptr)...); } /** Add components one by one to parameter pack */ template ::type* = nullptr> static void call_system(ecs_iter_t *iter, Func func, size_t index, Columns& columns, Targs... comps) { call_system(iter, func, index + 1, columns, comps..., columns[index]); } /** Callback provided to flecs */ static void run(ecs_iter_t *iter) { const Context *ctx = ecs_get(iter->world, iter->system, EcsContext); iter_invoker *self = (iter_invoker*)ctx->ctx; Func func = self->m_func; column_args columns(iter); call_system(iter, func, 0, columns.m_columns); } private: Func m_func; }; } // namespace _ //////////////////////////////////////////////////////////////////////////////// //// Persistent queries //////////////////////////////////////////////////////////////////////////////// class query_base { public: /** Get pointer to C query object. */ query_t* c_ptr() const { return m_query; } /** Sort the output of a query. * This enables sorting of entities across matched tables. As a result of this * operation, the order of entities in the matched tables may be changed. * Resorting happens when a query iterator is obtained, and only if the table * data has changed. * * If multiple queries that match the same (sub)set of tables specify different * sorting functions, resorting is likely to happen every time an iterator is * obtained, which can significantly slow down iterations. * * The sorting function will be applied to the specified component. Resorting * only happens if that component has changed, or when the entity order in the * table has changed. If no component is provided, resorting only happens when * the entity order changes. * * @tparam T The component used to sort. * @param compare The compare function used to sort the components. */ template void order_by(int(*compare)(flecs::entity_t, const T*, flecs::entity_t, const T*)) { ecs_query_order_by(m_world, m_query, flecs::_::component_info::id(m_world), (ecs_compare_action_t)compare); } /** Sort the output of a query. * Same as order_by, but with component identifier. * * @param component The component used to sort. * @param compare The compare function used to sort the components. */ void order_by(flecs::entity component, int(*compare)(flecs::entity_t, const void*, flecs::entity_t, const void*)) { ecs_query_order_by(m_world, m_query, component.id(), compare); } /** Group and sort matched tables. * Similar yo ecs_query_order_by, but instead of sorting individual entities, this * operation only sorts matched tables. This can be useful of a query needs to * enforce a certain iteration order upon the tables it is iterating, for * example by giving a certain component or tag a higher priority. * * The sorting function assigns a "rank" to each type, which is then used to * sort the tables. Tables with higher ranks will appear later in the iteration. * * Resorting happens when a query iterator is obtained, and only if the set of * matched tables for a query has changed. If table sorting is enabled together * with entity sorting, table sorting takes precedence, and entities will be * sorted within each set of tables that are assigned the same rank. * * @tparam T The component used to determine the group rank. * @param rank The rank action. */ template void group_by(int(*rank)(flecs::world_t*, flecs::entity_t, flecs::type_t type)) { ecs_query_group_by(m_world, m_query, flecs::_::component_info::id(m_world), rank); } /** Group and sort matched tables. * Same as group_by, but with component identifier. * * @param component The component used to determine the group rank. * @param rank The rank action. */ void group_by(flecs::entity component, int(*rank)(flecs::world_t*, flecs::entity_t, flecs::type_t type)) { ecs_query_group_by(m_world, m_query, component.id(), rank); } /** Returns whether the query data changed since the last iteration. * This operation must be invoked before obtaining the iterator, as this will * reset the changed state. The operation will return true after: * - new entities have been matched with * - matched entities were deleted * - matched components were changed * * @return true if entities changed, otherwise false. */ bool changed() { return ecs_query_changed(m_query); } /** Returns whether query is orphaned. * When the parent query of a subquery is deleted, it is left in an orphaned * state. The only valid operation on an orphaned query is deleting it. Only * subqueries can be orphaned. * * @return true if query is orphaned, otherwise false. */ bool orphaned() { return ecs_query_orphaned(m_query); } /** Free the query. */ void destruct() { ecs_query_free(m_query); m_world = nullptr; m_query = nullptr; } protected: world_t *m_world; query_t *m_query; }; template class query : public query_base { using Columns = typename _::column_args::Columns; public: query() { m_world = nullptr; m_query = nullptr; } explicit query(const world& world) { std::stringstream str; if (!_::pack_args_to_string(world.c_ptr(), str, true)) { ecs_abort(ECS_INVALID_PARAMETER, NULL); } m_world = world.c_ptr(); m_query = ecs_query_new(world.c_ptr(), str.str().c_str()); } explicit query(const world& world, query_base& parent) { std::stringstream str; if (!_::pack_args_to_string(world.c_ptr(), str, true)) { ecs_abort(ECS_INVALID_PARAMETER, NULL); } m_world = world.c_ptr(); m_query = ecs_subquery_new(world.c_ptr(), parent.c_ptr(), str.str().c_str()); } explicit query(const world& world, const char *expr) { std::stringstream str; m_world = world.c_ptr(); if (!_::pack_args_to_string(world.c_ptr(), str, true)) { m_query = ecs_query_new(world.c_ptr(), expr); } else { str << "," << expr; m_query = ecs_query_new(world.c_ptr(), str.str().c_str()); } } explicit query(const world& world, query_base& parent, const char *expr) { std::stringstream str; m_world = world.c_ptr(); if (!_::pack_args_to_string(world.c_ptr(), str, true)) { m_query = ecs_subquery_new(world.c_ptr(), parent.c_ptr(), expr); } else { str << "," << expr; m_query = ecs_subquery_new(world.c_ptr(), parent.c_ptr(), str.str().c_str()); } } query_iterator begin() const; query_iterator end() const; template void each(Func func) const { ecs_iter_t it = ecs_query_iter(m_query); while (ecs_query_next(&it)) { _::column_args columns(&it); _::each_invoker ctx(func); ctx.call_system(&it, func, 0, columns.m_columns); } } /* DEPRECATED */ template void action(Func func) const { ecs_iter_t it = ecs_query_iter(m_query); while (ecs_query_next(&it)) { _::column_args columns(&it); _::action_invoker ctx(func); ctx.call_system(&it, func, 0, columns.m_columns); } } template void iter(Func func) const { ecs_iter_t it = ecs_query_iter(m_query); while (ecs_query_next(&it)) { _::column_args columns(&it); _::iter_invoker ctx(func); ctx.call_system(&it, func, 0, columns.m_columns); } } }; //////////////////////////////////////////////////////////////////////////////// //// Snapshots make a copy of the world state that can be restored //////////////////////////////////////////////////////////////////////////////// class snapshot final { public: explicit snapshot(const world& world) : m_world( world ) , m_snapshot( nullptr ) { } snapshot(const snapshot& obj) : m_world( obj.m_world ) { ecs_iter_t it = ecs_snapshot_iter(obj.m_snapshot, nullptr); m_snapshot = ecs_snapshot_take_w_iter(&it, ecs_snapshot_next); } snapshot(snapshot&& obj) : m_world(obj.m_world) , m_snapshot(obj.m_snapshot) { obj.m_snapshot = nullptr; } snapshot& operator=(const snapshot& obj) { ecs_assert(m_world.c_ptr() == obj.m_world.c_ptr(), ECS_INVALID_PARAMETER, NULL); ecs_iter_t it = ecs_snapshot_iter(obj.m_snapshot, nullptr); m_snapshot = ecs_snapshot_take_w_iter(&it, ecs_snapshot_next); return *this; } snapshot& operator=(snapshot&& obj) { ecs_assert(m_world.c_ptr() == obj.m_world.c_ptr(), ECS_INVALID_PARAMETER, NULL); m_snapshot = obj.m_snapshot; obj.m_snapshot = nullptr; return *this; } void take() { if (m_snapshot) { ecs_snapshot_free(m_snapshot); } m_snapshot = ecs_snapshot_take(m_world.c_ptr()); } void take(flecs::filter filter) { if (m_snapshot) { ecs_snapshot_free(m_snapshot); } ecs_iter_t it = ecs_filter_iter(m_world.c_ptr(), filter.c_ptr()); m_snapshot = ecs_snapshot_take_w_iter( &it, ecs_filter_next); } void restore() { if (m_snapshot) { ecs_snapshot_restore(m_world.c_ptr(), m_snapshot); m_snapshot = nullptr; } } ~snapshot() { if (m_snapshot) { ecs_snapshot_free(m_snapshot); } } snapshot_t* c_ptr() const { return m_snapshot; } snapshot_filter filter(const filter& filter); filter_iterator begin(); filter_iterator end(); private: const world& m_world; snapshot_t *m_snapshot; }; //////////////////////////////////////////////////////////////////////////////// //// Fluent interface to run a system manually //////////////////////////////////////////////////////////////////////////////// class system_runner_fluent { public: system_runner_fluent(world_t *world, entity_t id, FLECS_FLOAT delta_time, void *param) : m_world(world) , m_id(id) , m_delta_time(delta_time) , m_param(param) , m_filter() , m_offset(0) , m_limit(0) { } system_runner_fluent& filter(filter filter) { m_filter = filter; return *this; } system_runner_fluent& offset(std::int32_t offset) { m_offset = offset; return *this; } system_runner_fluent& limit(std::int32_t limit) { m_limit = limit; return *this; } ~system_runner_fluent() { ecs_run_w_filter( m_world, m_id, m_delta_time, m_offset, m_limit, m_filter.c_ptr(), m_param); } private: world_t *m_world; entity_t m_id; FLECS_FLOAT m_delta_time; void *m_param; flecs::filter m_filter; std::int32_t m_offset; std::int32_t m_limit; }; //////////////////////////////////////////////////////////////////////////////// //// Register a system with Flecs //////////////////////////////////////////////////////////////////////////////// template class system final : public entity { public: explicit system(const flecs::world& world, const char *name = nullptr, const char *signature = nullptr) : entity(world, name) , m_kind(static_cast(OnUpdate)) , m_signature(signature) , m_interval(0.0) , m_on_demand(false) , m_hidden(false) , m_finalized(false) { ecs_assert(m_id != 0, ECS_INTERNAL_ERROR, NULL); } explicit system(const flecs::world& world, flecs::entity id) : entity(world, id.id()) , m_finalized(true) { } system& signature(const char *signature) { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); ecs_assert(!m_signature, ECS_INVALID_PARAMETER, NULL); m_signature = signature; return *this; } system& kind(entity_t kind) { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); m_kind = static_cast(kind); return *this; } system& interval(FLECS_FLOAT interval) { if (!m_finalized) { m_interval = interval; } else { ecs_set_interval(m_world, m_id, interval); } return *this; } FLECS_FLOAT interval() { return ecs_get_interval(m_world, m_id); } // DEPRECATED: use interval instead system& period(FLECS_FLOAT period) { return this->interval(period); } system& on_demand() { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); m_on_demand = true; return *this; } system& hidden() { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); m_hidden = true; return *this; } void enable() { ecs_enable(m_world, m_id, true); } void disable() { ecs_enable(m_world, m_id, false); } void set_period(FLECS_FLOAT period) const { ecs_set_interval(m_world, m_id, period); } void set_context(void *ctx) const { EcsContext ctx_value = { ctx }; ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); } void* get_context() const { const EcsContext *ctx = ecs_get(m_world, m_id, EcsContext); if (ctx) { return (void*)ctx->ctx; } else { return NULL; } } system_runner_fluent run(FLECS_FLOAT delta_time = 0.0f, void *param = nullptr) const { return system_runner_fluent(m_world, m_id, delta_time, param); } /* DEPRECATED. Use iter instead. */ template system& action(Func func) { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); auto ctx = new _::action_invoker(func); create_system(_::action_invoker::run, false); EcsContext ctx_value = {ctx}; ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); return *this; } /* Iter (or each) is mandatory and always the last thing that * is added in the fluent method chain. Create system signature from both * template parameters and anything provided by the signature method. */ template system& iter(Func func) { ecs_assert(!m_finalized, ECS_INVALID_PARAMETER, NULL); auto ctx = new _::iter_invoker(func); create_system(_::iter_invoker::run, false); EcsContext ctx_value = {ctx}; ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); return *this; } /* Each is similar to action, but accepts a function that operates on a * single entity */ template system& each(Func func) { auto ctx = new _::each_invoker(func); create_system(_::each_invoker::run, true); EcsContext ctx_value = {ctx}; ecs_set_ptr(m_world, m_id, EcsContext, &ctx_value); return *this; } ~system() = default; private: template entity_t create_system(Invoker invoker, bool is_each) { ecs_assert(m_id != 0, ECS_INTERNAL_ERROR, NULL); entity_t e; bool is_trigger = m_kind == flecs::OnAdd || m_kind == flecs::OnRemove; if (is_trigger) { // Don't add ANY source to each function if this is a trigger is_each = false; } std::string signature = build_signature(is_each); if (!signature.length()) { signature = "0"; } if (is_trigger) { e = ecs_new_trigger( m_world, m_id, nullptr, m_kind, signature.c_str(), invoker); } else { e = ecs_new_system( m_world, m_id, nullptr, m_kind, signature.c_str(), invoker); } ecs_assert(e == m_id, ECS_INTERNAL_ERROR, NULL); if (m_interval != 0) { ecs_set_interval(m_world, e, m_interval); } m_finalized = true; return e; } std::string build_signature(bool is_each) { bool is_set = false; std::stringstream str; if (_::pack_args_to_string(m_world, str, is_each)) { is_set = true; } if (m_signature) { if (is_set) { str << ","; } str << m_signature; is_set = true; } if (m_hidden) { if (is_set) { str << ","; } str << "SYSTEM:Hidden"; is_set = true; } if (m_on_demand) { if (is_set) { str << ","; } str << "SYSTEM:EcsOnDemand"; is_set = true; } return str.str(); } ecs_entity_t m_kind; const char *m_signature = nullptr; FLECS_FLOAT m_interval; bool m_on_demand; bool m_hidden; bool m_finalized; // After set to true, call no more fluent functions }; //////////////////////////////////////////////////////////////////////////////// //// Persistent queries //////////////////////////////////////////////////////////////////////////////// template class query_iterator { public: query_iterator() : m_has_next(false) , m_iter{ } { } query_iterator(const query& query) : m_iter( ecs_query_iter(query.c_ptr()) ) { m_has_next = ecs_query_next(&m_iter); } bool operator!=(query_iterator const& other) const { return m_has_next != other.m_has_next; } flecs::iter const operator*() const { return flecs::iter(&m_iter); } query_iterator& operator++() { m_has_next = ecs_query_next(&m_iter); return *this; } private: bool m_has_next; ecs_iter_t m_iter; }; //////////////////////////////////////////////////////////////////////////////// //// Utility for iterating over tables that match a filter //////////////////////////////////////////////////////////////////////////////// class filter_iterator { public: filter_iterator(ecs_iter_next_action_t action) : m_world(nullptr) , m_has_next(false) , m_iter{ } , m_action(action) { } filter_iterator(const world& world, const filter& filter, ecs_iter_next_action_t action) : m_world( world.c_ptr() ) , m_iter( ecs_filter_iter(m_world, filter.c_ptr()) ) , m_action(action) { m_has_next = m_action(&m_iter); } filter_iterator(const world& world, const snapshot& snapshot, const filter& filter, ecs_iter_next_action_t action) : m_world( world.c_ptr() ) , m_iter( ecs_snapshot_iter(snapshot.c_ptr(), filter.c_ptr()) ) , m_action(action) { m_has_next = m_action(&m_iter); } bool operator!=(filter_iterator const& other) const { return m_has_next != other.m_has_next; } flecs::iter const operator*() const { return flecs::iter(&m_iter); } filter_iterator& operator++() { m_has_next = m_action(&m_iter); return *this; } private: world_t *m_world; bool m_has_next; ecs_iter_t m_iter; ecs_iter_next_action_t m_action; }; //////////////////////////////////////////////////////////////////////////////// //// Tree iterator //////////////////////////////////////////////////////////////////////////////// class tree_iterator { public: tree_iterator() : m_has_next(false) , m_iter{ } { } tree_iterator(flecs::entity entity) : m_iter( ecs_scope_iter(entity.world().c_ptr(), entity.id()) ) { m_has_next = ecs_scope_next(&m_iter); } bool operator!=(tree_iterator const& other) const { return m_has_next != other.m_has_next; } flecs::iter const operator*() const { return flecs::iter(&m_iter); } tree_iterator& operator++() { m_has_next = ecs_scope_next(&m_iter); return *this; } private: bool m_has_next; ecs_iter_t m_iter; }; //////////////////////////////////////////////////////////////////////////////// //// Utility for creating a world-based filter iterator //////////////////////////////////////////////////////////////////////////////// class world_filter { public: world_filter(const world& world, const filter& filter) : m_world( world ) , m_filter( filter ) { } inline filter_iterator begin() const { return filter_iterator(m_world, m_filter, ecs_filter_next); } inline filter_iterator end() const { return filter_iterator(ecs_filter_next); } private: const world& m_world; const filter& m_filter; }; //////////////////////////////////////////////////////////////////////////////// //// Utility for creating a snapshot-based filter iterator //////////////////////////////////////////////////////////////////////////////// class snapshot_filter { public: snapshot_filter(const world& world, const snapshot& snapshot, const filter& filter) : m_world( world ) , m_snapshot( snapshot ) , m_filter( filter ) { } inline filter_iterator begin() const { return filter_iterator(m_world, m_snapshot, m_filter, ecs_snapshot_next); } inline filter_iterator end() const { return filter_iterator(ecs_snapshot_next); } private: const world& m_world; const snapshot& m_snapshot; const filter& m_filter; }; //////////////////////////////////////////////////////////////////////////////// //// Utility for creating a child table iterator //////////////////////////////////////////////////////////////////////////////// class child_iterator { public: child_iterator(const entity& entity) : m_parent( entity ) { } inline tree_iterator begin() const { return tree_iterator(m_parent); } inline tree_iterator end() const { return tree_iterator(); } private: const entity& m_parent; }; //////////////////////////////////////////////////////////////////////////////// //// Reader for world/snapshot serialization //////////////////////////////////////////////////////////////////////////////// class reader final { public: explicit reader(world& world) { m_reader = ecs_reader_init(world.c_ptr()); } reader(world& world, snapshot& snapshot) { (void)world; ecs_iter_t it = ecs_snapshot_iter(snapshot.c_ptr(), nullptr); m_reader = ecs_reader_init_w_iter(&it, ecs_snapshot_next); } int32_t read(char *buffer, std::int64_t size) { return ecs_reader_read(buffer, static_cast(size), &m_reader); } private: ecs_reader_t m_reader; }; //////////////////////////////////////////////////////////////////////////////// //// Writer for world deserialization //////////////////////////////////////////////////////////////////////////////// class writer final { public: explicit writer(world& world) { m_writer = ecs_writer_init(world.c_ptr()); } int32_t write(const char *buffer, std::int64_t size) { return ecs_writer_write(buffer, static_cast(size), &m_writer); } private: ecs_writer_t m_writer; }; //////////////////////////////////////////////////////////////////////////////// //// Filter fwd declared functions //////////////////////////////////////////////////////////////////////////////// inline snapshot_filter snapshot::filter(const flecs::filter& filter) { return snapshot_filter(m_world, *this, filter); } inline filter_iterator snapshot::begin() { return filter_iterator(m_world, *this, flecs::filter(m_world), ecs_snapshot_next); } inline filter_iterator snapshot::end() { return filter_iterator(ecs_snapshot_next); } //////////////////////////////////////////////////////////////////////////////// //// Query fwd declared functions //////////////////////////////////////////////////////////////////////////////// template inline query_iterator query::begin() const { return query_iterator(*this); } template inline query_iterator query::end() const { return query_iterator(); } //////////////////////////////////////////////////////////////////////////////// //// Cached ptr fwd declared functions //////////////////////////////////////////////////////////////////////////////// template flecs::entity ref::entity() const { return flecs::entity(m_world, m_entity); } //////////////////////////////////////////////////////////////////////////////// //// Entity fwd declared functions //////////////////////////////////////////////////////////////////////////////// inline flecs::type entity::type() const { return flecs::type(m_world, ecs_get_type(m_world, m_id)); } inline flecs::type entity::to_type() const { ecs_type_t type = ecs_type_from_entity(m_world, m_id); return flecs::type(m_world, type); } inline child_iterator entity::children() const { return flecs::child_iterator(*this); } //////////////////////////////////////////////////////////////////////////////// //// Entity fluent fwd declared functions //////////////////////////////////////////////////////////////////////////////// template inline typename entity_builder::base_type& entity_builder::add(const entity& entity) const { return add(entity.id()); } template inline typename entity_builder::base_type& entity_builder::add(type type) const { return add(type.c_ptr()); } template template inline typename entity_builder::base_type& entity_builder::add_trait(flecs::entity component) const { return add_trait(_::component_info::id(), component.id()); } template template inline typename entity_builder::base_type& entity_builder::add_trait_tag(flecs::entity trait) const { return add_trait(trait.id(), _::component_info::id()); } template inline typename entity_builder::base_type& entity_builder::add_trait(flecs::entity trait, flecs::entity entity) const { return add_trait(trait.id(), entity.id()); } template inline typename entity_builder::base_type& entity_builder::remove(const entity& entity) const { return remove(entity.id()); } template inline typename entity_builder::base_type& entity_builder::remove(type type) const { return remove(type.c_ptr()); } template template inline typename entity_builder::base_type& entity_builder::remove_trait(flecs::entity component) const { return remove_trait(_::component_info::id(), component.id()); } template template inline typename entity_builder::base_type& entity_builder::remove_trait_tag(flecs::entity trait) const { return remove_trait(trait.id(), _::component_info::id()); } template inline typename entity_builder::base_type& entity_builder::remove_trait(flecs::entity trait, flecs::entity entity) const { return remove_trait(trait.id(), entity.id()); } template inline typename entity_builder::base_type& entity_builder::add_childof(const entity& entity) const { return add_childof(entity.id()); } template inline typename entity_builder::base_type& entity_builder::remove_childof(const entity& entity) const { return remove_childof(entity.id()); } template inline typename entity_builder::base_type& entity_builder::add_instanceof(const entity& entity) const { return add_instanceof(entity.id()); } template inline typename entity_builder::base_type& entity_builder::remove_instanceof(const entity& entity) const { return remove_instanceof(entity.id()); } template template inline typename entity_builder::base_type& entity_builder::set_trait_tag(flecs::entity trait, const C& value) const { static_cast(this)->invoke( [trait, &value](world_t *world, entity_t id) { ecs_set_ptr_w_entity(world, id, ecs_trait(_::component_info::id(world), trait.id()), sizeof(C), &value); }); return *static_cast(this); } template template inline typename entity_builder::base_type& entity_builder::set_trait(const T& value, flecs::entity tag) const { static_cast(this)->invoke( [tag, &value](world_t *world, entity_t id) { ecs_set_ptr_w_entity(world, id, ecs_trait(tag.id(), _::component_info::id(world)), sizeof(T), &value); }); return *static_cast(this); } template inline typename entity_builder::base_type& entity_builder::add_owned(flecs::type type) const { return add_owned(type.id()); } template inline typename entity_builder::base_type& entity_builder::add_switch(const entity& sw) const { return add_switch(sw.id()); } template inline typename entity_builder::base_type& entity_builder::add_switch(const type& sw) const { return add_switch(sw.id()); } template inline typename entity_builder::base_type& entity_builder::remove_switch(const entity& sw) const { return remove_switch(sw.id()); } template inline typename entity_builder::base_type& entity_builder::remove_switch(const type& sw) const { return remove_switch(sw.id()); } template inline typename entity_builder::base_type& entity_builder::add_case(const entity& sw_case) const { return add_case(sw_case.id()); } template inline typename entity_builder::base_type& entity_builder::remove_case(const entity& sw_case) const { return remove_case(sw_case.id()); } template inline typename entity_builder::base_type& entity_builder::enable(const entity& e) const { return enable(e.id()); } template inline typename entity_builder::base_type& entity_builder::disable(const entity& e) const { return disable(e.id()); } inline bool entity::has_switch(flecs::type type) const { return ecs_has_entity(m_world, m_id, flecs::Switch | type.id()); } inline flecs::entity entity::get_case(flecs::type sw) const { return flecs::entity(m_world, ecs_get_case(m_world, m_id, sw.id())); } //////////////////////////////////////////////////////////////////////////////// //// Iter fwd declared functions //////////////////////////////////////////////////////////////////////////////// inline flecs::entity iter::system() const { return flecs::entity(m_iter->world, m_iter->system); } inline flecs::world iter::world() const { return flecs::world(m_iter->world); } inline flecs::entity iter::entity(size_t row) const { ecs_assert(row < (size_t)m_iter->count, ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); return flecs::entity(m_iter->world, m_iter->entities[row]); } /* Obtain column source (0 if self) */ inline flecs::entity iter::column_source(int32_t col) const { return flecs::entity(m_iter->world, ecs_column_source(m_iter, col)); } /* Obtain component/tag entity of column */ inline flecs::entity iter::column_entity(int32_t col) const { return flecs::entity(m_iter->world, ecs_column_entity(m_iter, col)); } /* Obtain type of column */ inline type iter::column_type(int32_t col) const { return flecs::type(m_iter->world, ecs_column_type(m_iter, col)); } /* Obtain type of table being iterated over */ inline type iter::table_type() const { return flecs::type(m_iter->world, ecs_iter_type(m_iter)); } //////////////////////////////////////////////////////////////////////////////// //// World fwd declared functions //////////////////////////////////////////////////////////////////////////////// inline void world::delete_entities(flecs::filter filter) const { ecs_bulk_delete(m_world, filter.c_ptr()); } template inline void world::add(flecs::filter filter) const { ecs_bulk_add_remove_type( m_world, _::component_info::type(m_world), nullptr, filter.c_ptr()); } inline void world::add(flecs::type t) const { ecs_bulk_add_remove_type(m_world, t.c_ptr(), nullptr, nullptr); } inline void world::add(flecs::type t, flecs::filter filter) const { ecs_bulk_add_remove_type(m_world, t.c_ptr(), nullptr, filter.c_ptr()); } inline void world::add(class flecs::entity e) const { ecs_bulk_add_remove_type(m_world, e.to_type().c_ptr(), nullptr, nullptr); } inline void world::add(class flecs::entity e, flecs::filter filter) const { ecs_bulk_add_remove_type(m_world, e.to_type().c_ptr(), nullptr, filter.c_ptr()); } template inline void world::remove(flecs::filter filter) const { ecs_bulk_add_remove_type( m_world, nullptr, _::component_info::type(m_world), filter.c_ptr()); } inline void world::remove(flecs::type t) const { ecs_bulk_add_remove_type(m_world, nullptr, t.c_ptr(), nullptr); } inline void world::remove(flecs::type t, flecs::filter filter) const { ecs_bulk_add_remove_type(m_world, nullptr, t.c_ptr(), filter.c_ptr()); } inline void world::remove(class entity e) const { ecs_bulk_add_remove_type(m_world, nullptr, e.to_type().c_ptr(), nullptr); } inline void world::remove(class entity e, flecs::filter filter) const { ecs_bulk_add_remove_type(m_world, nullptr, e.to_type().c_ptr(), filter.c_ptr()); } inline flecs::world_filter world::filter(const flecs::filter& filter) const { return flecs::world_filter(*this, filter); } inline filter_iterator world::begin() const { return filter_iterator(*this, flecs::filter(*this), ecs_filter_next); } inline filter_iterator world::end() const { return filter_iterator(ecs_filter_next); } inline int world::count(flecs::filter filter) const { return ecs_count_w_filter(m_world, filter.c_ptr()); } inline void world::init_builtin_components() { pod_component("flecs::core::Component"); pod_component("flecs::core::Type"); pod_component("flecs::core::Name"); } template inline flecs::entity world::use(const char *alias) { entity_t id = _::component_info::id(m_world); const char *name = alias; if (!name) { // If no name is defined, use the entity name without the scope name = ecs_get_name(m_world, id); } ecs_use(m_world, id, name); return flecs::entity(m_world, id); } inline flecs::entity world::use(const char *name, const char *alias) { entity_t id = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::"); ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL); ecs_use(m_world, id, alias); return flecs::entity(m_world, id); } inline void world::use(flecs::entity e, const char *alias) { entity_t id = e.id(); const char *name = alias; if (!name) { // If no name is defined, use the entity name without the scope ecs_get_name(m_world, id); } ecs_use(m_world, id, alias); } inline entity world::lookup(const char *name) const { auto id = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::"); return flecs::entity(*this, id); } inline entity world::lookup(std::string& name) const { auto id = ecs_lookup_path_w_sep(m_world, 0, name.c_str(), "::", "::"); return flecs::entity(*this, id); } template void world::set(T value) const { flecs::entity e(m_world, _::component_info::id(m_world)); e.set(value); } template T* world::get_mut() const { flecs::entity e(m_world, _::component_info::id(m_world)); return e.get_mut(); } template void world::modified() const { flecs::entity e(m_world, _::component_info::id(m_world)); return e.modified(); } template void world::patch(std::function func) const { flecs::entity e(m_world, _::component_info::id(m_world)); e.patch(func); } template const T* world::get() const { flecs::entity e(m_world, _::component_info::id(m_world)); return e.get(); } template bool world::has() const { flecs::entity e(m_world, _::component_info::id(m_world)); return e.has(); } template void world::remove() const { flecs::entity e(m_world, _::component_info::id(m_world)); e.remove(); } template flecs::entity world::singleton() { return flecs::entity(m_world, _::component_info::id(m_world)); } template inline flecs::entity world::entity(Args &&... args) const { return flecs::entity(*this, std::forward(args)...); } template inline flecs::entity world::prefab(Args &&... args) const { return flecs::prefab(*this, std::forward(args)...); } template inline flecs::type world::type(Args &&... args) const { return flecs::type(*this, std::forward(args)...); } template inline flecs::system world::system(Args &&... args) const { return flecs::system(*this, std::forward(args)...); } template inline flecs::query world::query(Args &&... args) const { return flecs::query(*this, std::forward(args)...); } template inline flecs::entity world::module(Args &&... args) const { return flecs::module(*this, std::forward(args)...); } template inline flecs::entity world::import() { return flecs::import(*this); } template inline flecs::entity world::component(Args &&... args) const { return flecs::component(*this, std::forward(args)...); } template inline flecs::entity world::pod_component(Args &&... args) const { return flecs::pod_component(*this, std::forward(args)...); } template inline flecs::entity world::relocatable_component(Args &&... args) const { return flecs::relocatable_component(*this, std::forward(args)...); } template inline flecs::snapshot world::snapshot(Args &&... args) const { return flecs::snapshot(*this, std::forward(args)...); } /** Utilities to convert type trait to flecs signature syntax */ namespace _ { template ::value == true, void>::type* = nullptr> constexpr const char *inout_modifier() { return "[in] "; } template ::value == true, void>::type* = nullptr> constexpr const char *inout_modifier() { return "[out] "; } template ::value == false && std::is_reference::value == false, void>::type* = nullptr> constexpr const char *inout_modifier() { return ""; } template ::value == true, void>::type* = nullptr> constexpr const char *optional_modifier() { return "?"; } template ::value == false, void>::type* = nullptr> constexpr const char *optional_modifier() { return ""; } /** Convert template arguments to string */ template bool pack_args_to_string(world_t *world, std::stringstream& str, bool is_each) { (void)world; std::array ids = { (_::component_info::name(world))... }; std::array inout_modifiers = { (inout_modifier())... }; std::array optional_modifiers = { (optional_modifier())... }; size_t i = 0; for (auto id : ids) { if (i) { str << ","; } str << inout_modifiers[i]; str << optional_modifiers[i]; if (is_each) { str << "ANY:"; } str << id; i ++; } return i != 0; } } // namespace _ } // namespace flecs #endif #endif #endif #endif