#define flecs_STATIC /** * @file flecs.h * @brief Flecs public API. * * This file contains the public API for Flecs. */ #ifndef FLECS_H #define FLECS_H /* FLECS_LEGACY should be defined when building for C89 */ // #define FLECS_LEGACY /* FLECS_NO_DEPRECATED_WARNINGS disables deprecated warnings */ #define FLECS_NO_DEPRECATED_WARNINGS /* 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 /* FLECS_SANITIZE enables expensive checks that can detect issues early */ #ifndef NDEBUG #define FLECS_SANITIZE #endif /* If this is a regular, non-custom build, build all modules and addons. */ #ifndef FLECS_CUSTOM_BUILD /* Modules */ #define FLECS_SYSTEM #define FLECS_PIPELINE #define FLECS_TIMER /* Addons */ #define FLECS_BULK #define FLECS_MODULE #define FLECS_PARSER #define FLECS_PLECS #define FLECS_QUEUE #define FLECS_SNAPSHOT #define FLECS_DIRECT_ACCESS #define FLECS_STATS #endif // ifndef FLECS_CUSTOM_BUILD /* Unconditionally include deprecated definitions until the rest of the codebase * has caught up */ #define FLECS_DEPRECATED /* 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 // FLECS_FLOAT /** * @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 /* Some symbols are only exported when building in debug build, to enable * whitebox testing of internal datastructures */ #ifndef NDEBUG #define FLECS_DBG_API FLECS_API #else #define FLECS_DBG_API #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_CAST(ecs_size_t, sizeof(T)) /* Use alignof in C++, or a trick in C. */ #ifdef __cplusplus #define ECS_ALIGNOF(T) static_cast(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 #ifndef FLECS_NO_DEPRECATED_WARNINGS #if defined(__GNUC__) #define ECS_DEPRECATED(msg) __attribute__((deprecated(msg))) #elif defined(_MSC_VER) #define ECS_DEPRECATED(msg) __declspec(deprecated(msg)) #else #define ECS_DEPRECATED(msg) #endif #else #define ECS_DEPRECATED(msg) #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) /* Abstraction on top of C-style casts so that C functions can be used in C++ * code without producing warnings */ #ifndef __cplusplus #define ECS_CAST(T, V) ((T)(V)) #else #define ECS_CAST(T, V) (static_cast(V)) #endif //////////////////////////////////////////////////////////////////////////////// //// Reserved component ids //////////////////////////////////////////////////////////////////////////////// /** Builtin component ids */ #define FLECS__EEcsComponent (1) #define FLECS__EEcsComponentLifecycle (2) #define FLECS__EEcsType (3) #define FLECS__EEcsIdentifier (4) #define FLECS__EEcsTrigger (6) #define FLECS__EEcsQuery (7) #define FLECS__EEcsObserver (8) // #define FLECS__EEcsIterable (9) /* System module component ids */ #define FLECS__EEcsSystem (10) #define FLECS__EEcsTickSource (11) /** Pipeline module component ids */ #define FLECS__EEcsPipelineQuery (12) /** Timer module component ids */ #define FLECS__EEcsTimer (13) #define FLECS__EEcsRateFilter (14) //////////////////////////////////////////////////////////////////////////////// //// Entity id macro's //////////////////////////////////////////////////////////////////////////////// #define ECS_ROLE_MASK (0xFFull << 56) #define ECS_ENTITY_MASK (0xFFFFFFFFull) #define ECS_GENERATION_MASK (0xFFFFull << 32) #define ECS_GENERATION(e) ((e & ECS_GENERATION_MASK) >> 32) #define ECS_GENERATION_INC(e) ((e & ~ECS_GENERATION_MASK) | ((0xFFFF & (ECS_GENERATION(e) + 1)) << 32)) #define ECS_COMPONENT_MASK (~ECS_ROLE_MASK) #define ECS_HAS_ROLE(e, role) ((e & ECS_ROLE_MASK) == ECS_##role) #define ECS_PAIR_RELATION(e) (ecs_entity_t_hi(e & ECS_COMPONENT_MASK)) #define ECS_PAIR_OBJECT(e) (ecs_entity_t_lo(e)) #define ECS_HAS_RELATION(e, rel) (ECS_HAS_ROLE(e, PAIR) && (ECS_PAIR_RELATION(e) == rel)) #define ECS_HAS_PAIR_OBJECT(e, rel, obj)\ (ECS_HAS_RELATION(e, rel) && ECS_PAIR_OBJECT(e) == obj) #define ECS_HAS(id, has_id)(\ (id == has_id) ||\ (ECS_HAS_PAIR_OBJECT(id, ECS_PAIR_RELATION(has_id), ECS_PAIR_OBJECT(has_id)))) //////////////////////////////////////////////////////////////////////////////// //// 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 id. */ #define ecs_id(T) FLECS__E##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 //////////////////////////////////////////////////////////////////////////////// //// Utilities for working with pair identifiers //////////////////////////////////////////////////////////////////////////////// #define ecs_entity_t_lo(value) ECS_CAST(uint32_t, value) #define ecs_entity_t_hi(value) ECS_CAST(uint32_t, (value) >> 32) #define ecs_entity_t_comb(lo, hi) ((ECS_CAST(uint64_t, hi) << 32) + ECS_CAST(uint32_t, lo)) #define ecs_pair(pred, obj) (ECS_PAIR | ecs_entity_t_comb(obj, pred)) /* Get object from pair with the correct (current) generation count */ #define ecs_pair_relation(world, pair) ecs_get_alive(world, ECS_PAIR_RELATION(pair)) #define ecs_pair_object(world, pair) ecs_get_alive(world, ECS_PAIR_OBJECT(pair)) //////////////////////////////////////////////////////////////////////////////// //// Convenience macro's for ctor, dtor, move and copy //////////////////////////////////////////////////////////////////////////////// #ifndef FLECS_LEGACY /* 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__\ }\ } /* Constructor / destructor convenience macro */ #define ECS_ON_SET_IMPL(type, var, ...)\ void type##_##on_set(\ 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__\ }\ } #endif //////////////////////////////////////////////////////////////////////////////// //// Error codes //////////////////////////////////////////////////////////////////////////////// #define ECS_INVALID_OPERATION (1) #define ECS_INVALID_PARAMETER (2) #define ECS_INVALID_DELETE (3) #define ECS_OUT_OF_MEMORY (4) #define ECS_OUT_OF_RANGE (5) #define ECS_UNSUPPORTED (6) #define ECS_INTERNAL_ERROR (7) #define ECS_ALREADY_DEFINED (8) #define ECS_MISSING_OS_API (9) #define ECS_THREAD_ERROR (10) #define ECS_CYCLE_DETECTED (11) #define ECS_INCONSISTENT_NAME (20) #define ECS_NAME_IN_USE (21) #define ECS_NOT_A_COMPONENT (22) #define ECS_INVALID_COMPONENT_SIZE (23) #define ECS_INVALID_COMPONENT_ALIGNMENT (24) #define ECS_COMPONENT_NOT_REGISTERED (25) #define ECS_INCONSISTENT_COMPONENT_ID (26) #define ECS_INCONSISTENT_COMPONENT_ACTION (27) #define ECS_MODULE_UNDEFINED (28) #define ECS_COLUMN_ACCESS_VIOLATION (40) #define ECS_COLUMN_INDEX_OUT_OF_RANGE (41) #define ECS_COLUMN_IS_NOT_SHARED (42) #define ECS_COLUMN_IS_SHARED (43) #define ECS_COLUMN_HAS_NO_DATA (44) #define ECS_COLUMN_TYPE_MISMATCH (45) #define ECS_NO_OUT_COLUMNS (46) #define ECS_TYPE_NOT_AN_ENTITY (60) #define ECS_TYPE_CONSTRAINT_VIOLATION (61) #define ECS_TYPE_INVALID_CASE (62) #define ECS_INVALID_WHILE_ITERATING (70) #define ECS_LOCKED_STORAGE (71) #define ECS_INVALID_FROM_WORKER (72) #define ECS_DESERIALIZE_FORMAT_ERROR (80) //////////////////////////////////////////////////////////////////////////////// //// Deprecated constants //////////////////////////////////////////////////////////////////////////////// /* These constants should no longer be used, but are required by the core to * guarantee backwards compatibility */ #define ECS_AND (ECS_ROLE | (0x79ull << 56)) #define ECS_OR (ECS_ROLE | (0x78ull << 56)) #define ECS_XOR (ECS_ROLE | (0x77ull << 56)) #define ECS_NOT (ECS_ROLE | (0x76ull << 56)) #ifdef __cplusplus } #endif #endif /** * @file log.h * @brief Internal logging API. * * 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_fatal( const char *file, int32_t line, const char *fmt, ...); FLECS_API void _ecs_deprecated( const char *file, int32_t line, const char *msg); 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__) #define ecs_fatal(...)\ _ecs_fatal(__FILE__, __LINE__, __VA_ARGS__) #ifndef FLECS_NO_DEPRECATED_WARNINGS #define ecs_deprecated(...)\ _ecs_deprecated(__FILE__, __LINE__, __VA_ARGS__) #else #define ecs_deprecated(...) #endif /* 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 *file, int32_t line, const char *fmt, ...); #define ecs_abort(error_code, ...)\ _ecs_abort(error_code, __FILE__, __LINE__, __VA_ARGS__); abort() /** Assert */ FLECS_API void _ecs_assert( bool condition, int32_t error_code, const char *condition_str, const char *file, int32_t line, const char *fmt, ...); #ifdef NDEBUG #define ecs_assert(condition, error_code, ...) #else #define ecs_assert(condition, error_code, ...)\ _ecs_assert(condition, error_code, #condition, __FILE__, __LINE__, __VA_ARGS__);\ 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 vector.h * @brief Vector datastructure. * * 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_CAST(int16_t, 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)) #define ecs_vector_last_t(vector, size, alignment) \ _ecs_vector_last(vector, ECS_VECTOR_U(size, alignment)) /** 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( ecs_vector_t *vector, ecs_size_t elem_size, int16_t offset, int32_t index); #define ecs_vector_remove(vector, T, index) \ _ecs_vector_remove(vector, ECS_VECTOR_T(T), index) #define ecs_vector_remove_t(vector, size, alignment, index) \ _ecs_vector_remove(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; }; /* C++ class mainly used as wrapper around internal ecs_vector_t. Do not use * this class as a replacement for STL datastructures! */ template class vector { public: explicit vector(ecs_vector_t *v) : m_vector( v ) { } vector(size_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 *static_cast(_ecs_vector_get(m_vector, ECS_VECTOR_T(T), index)); } vector_iterator begin() { return vector_iterator( static_cast(_ecs_vector_first(m_vector, ECS_VECTOR_T(T))), 0); } vector_iterator end() { return vector_iterator( static_cast(_ecs_vector_last(m_vector, ECS_VECTOR_T(T))), ecs_vector_count(m_vector)); } void clear() { ecs_vector_clear(m_vector); } void destruct() { ecs_vector_free(m_vector); } void add(T& value) { T* elem = static_cast(_ecs_vector_add(&m_vector, ECS_VECTOR_T(T))); *elem = value; } void add(T&& value) { T* elem = static_cast(_ecs_vector_add(&m_vector, ECS_VECTOR_T(T))) *elem = value; } T& get(int32_t index) { ecs_assert(index < ecs_vector_count(m_vector), ECS_OUT_OF_RANGE, NULL); return *static_cast(_ecs_vector_get(m_vector, ECS_VECTOR_T(T), index)); } T& first() { return *static_cast(_ecs_vector_first(m_vector, ECS_VECTOR_T(T))); } T& last() { return *static_cast(_ecs_vector_last(m_vector, ECS_VECTOR_T(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(size_t count) { m_vector = ecs_vector_new(T, static_cast(count)); } ecs_vector_t *m_vector; }; } #endif #endif #endif /** * @file map.h * @brief Map datastructure. * * 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 uint64_t ecs_map_key_t; typedef struct ecs_map_iter_t { const ecs_map_t *map; struct 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) /** Test if map has key */ FLECS_API bool ecs_map_has( const ecs_map_t *map, ecs_map_key_t 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 #include namespace flecs { /* C++ class mainly used as wrapper around internal ecs_map_t. Do not use * this class as a replacement for STL datastructures! */ template class map { public: map(size_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, sizeof(T), reinterpret_cast(key), &value); } T& get(K& key) { static_cast(_ecs_map_get(m_map, sizeof(T), reinterpret_cast(key))); } void destruct() { ecs_map_free(m_map); } private: void init(size_t count) { m_map = ecs_map_new(T, static_cast(count)); } ecs_map_t *m_map; }; } #endif #endif #endif /** * @file strbuf.h * @brief Utility for constructing strings. * * 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. */ #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) 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 /** * @file os_api.h * @brief Operationg system abstractions. * * This file contains the operating system abstraction API. The flecs core * library avoids OS/runtime specific API calls as much as possible. Instead it * provides an interface that can be implemented by applications. * * Examples for how to implement this interface can be found in the * examples/os_api folder. */ #ifndef FLECS_OS_API_H #define FLECS_OS_API_H #include #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 */ #ifndef ecs_os_malloc #define ecs_os_malloc(size) ecs_os_api.malloc_(size) #endif #ifndef ecs_os_free #define ecs_os_free(ptr) ecs_os_api.free_(ptr) #endif #ifndef ecs_os_realloc #define ecs_os_realloc(ptr, size) ecs_os_api.realloc_(ptr, size) #endif #ifndef ecs_os_calloc #define ecs_os_calloc(size) ecs_os_api.calloc_(size) #endif #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 #define ecs_os_malloc_t(T) (T*)(ecs_os_malloc(ECS_SIZEOF(T))) #define ecs_os_malloc_n(T, count) (T*)(ecs_os_malloc(ECS_SIZEOF(T) * count)) #define ecs_os_calloc_t(T) (T*)(ecs_os_calloc(ECS_SIZEOF(T))) #define ecs_os_calloc_n(T, count) (T*)(ecs_os_calloc(ECS_SIZEOF(T) * count)) #define ecs_os_alloca_t(T) (T*)(ecs_os_alloca(ECS_SIZEOF(T))) #define ecs_os_alloca_n(T, count) (T*)(ecs_os_alloca(ECS_SIZEOF(T) * count)) /* Strings */ #ifndef ecs_os_strdup #define ecs_os_strdup(str) ecs_os_api.strdup_(str) #endif #define ecs_os_strset(dst, src) ecs_os_free(*dst); *dst = ecs_os_strdup(src) #ifdef __cplusplus #define ecs_os_strlen(str) static_cast(strlen(str)) #define ecs_os_strncmp(str1, str2, num) strncmp(str1, str2, static_cast(num)) #define ecs_os_memcmp(ptr1, ptr2, num) memcmp(ptr1, ptr2, static_cast(num)) #define ecs_os_memcpy(ptr1, ptr2, num) memcpy(ptr1, ptr2, static_cast(num)) #define ecs_os_memset(ptr, value, num) memset(ptr, value, static_cast(num)) #define ecs_os_memmove(ptr, value, num) memmove(ptr, value, static_cast(num)) #else #define ecs_os_strlen(str) (ecs_size_t)strlen(str) #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)) #endif #define ecs_os_memcpy_t(ptr1, ptr2, T) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T)) #define ecs_os_memcpy_n(ptr1, ptr2, T, count) ecs_os_memcpy(ptr1, ptr2, ECS_SIZEOF(T) * count) #define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #define ecs_os_memset_t(ptr, value, T) ecs_os_memset(ptr, value, ECS_SIZEOF(T)) #define ecs_os_memset_n(ptr, value, T, count) ecs_os_memset(ptr, value, ECS_SIZEOF(T) * count) #define ecs_os_strcmp(str1, str2) strcmp(str1, str2) #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) #ifdef __cplusplus #define ecs_os_strncpy(str1, str2, num) strncpy_s(str1, INT_MAX, str2, static_cast(num)) #else #define ecs_os_strncpy(str1, str2, num) strncpy_s(str1, INT_MAX, str2, (size_t)(num)) #endif #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) #ifdef __cplusplus #define ecs_os_strncpy(str1, str2, num) strncpy(str1, str2, static_cast(num)) #else #define ecs_os_strncpy(str1, str2, num) strncpy(str1, str2, (size_t)(num)) #endif #endif /* Files */ #if defined(_MSC_VER) #define ecs_os_fopen(result, file, mode) fopen_s(result, file, mode) #else #define ecs_os_fopen(result, file, mode) (*(result)) = fopen(file, mode) #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, ...); FLECS_API const char* ecs_os_strerror(int err); /* 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 * @{ */ /** Pointer object returned by API. */ typedef void ecs_object_t; /** An id. Ids are the things that can be added to an entity. An id can be an * entity or pair, and can have an optional role. */ typedef uint64_t ecs_id_t; /** An entity identifier. */ typedef ecs_id_t ecs_entity_t; /** A vector containing component identifiers used to describe a type. */ typedef const ecs_vector_t* ecs_type_t; /** A world is the container for all ECS data and supporting features. */ typedef struct ecs_world_t ecs_world_t; /** A query allows for cached iteration over ECS data */ typedef struct ecs_query_t ecs_query_t; /** A filter allows for uncached, ad hoc iteration over ECS data */ typedef struct ecs_filter_t ecs_filter_t; /** A trigger reacts to events matching a single filter term */ typedef struct ecs_trigger_t ecs_trigger_t; /** An observer reacts to events matching multiple filter terms */ typedef struct ecs_observer_t ecs_observer_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; /** @} */ /** * @defgroup constants API constants * @{ */ /* Maximum number of components to add/remove in a single operation */ #define ECS_MAX_ADD_REMOVE (32) /* Maximum number of terms cached in static arrays */ #define ECS_TERM_CACHE_SIZE (8) /* Maximum number of events to set in static array of trigger descriptor */ #define ECS_TRIGGER_DESC_EVENT_COUNT_MAX (8) /** @} */ /** * @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); /** Callback used for sorting components */ typedef int (*ecs_order_by_action_t)( ecs_entity_t e1, const void *ptr1, ecs_entity_t e2, const void *ptr2); /** Callback used for ranking types */ typedef int32_t (*ecs_group_by_action_t)( ecs_world_t *world, ecs_type_t type, ecs_id_t id, void *ctx); /** 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); /** Function to cleanup context data */ typedef void (*ecs_ctx_free_t)( void *ctx); /** Callback used for sorting values */ typedef int (*ecs_compare_action_t)( const void *ptr1, const void *ptr2); /** Callback used for hashing values */ typedef uint64_t (*ecs_hash_value_action_t)( const void *ptr); /** @} */ /** * @defgroup filter_types Types used to describe filters, terms and triggers * @{ */ /** Set flags describe if & how a matched entity should be substituted */ #define EcsDefaultSet (0) /* Default set, SuperSet|Self for This subject */ #define EcsSelf (1) /* Select self (inclusive) */ #define EcsSuperSet (2) /* Select superset until predicate match */ #define EcsSubSet (4) /* Select subset until predicate match */ #define EcsCascade (8) /* Use breadth-first ordering of relations */ #define EcsAll (16) /* Walk full super/subset, regardless of match */ #define EcsNothing (32) /* Select from nothing */ /** Specify read/write access for term */ typedef enum ecs_inout_kind_t { EcsInOutDefault, EcsInOut, EcsIn, EcsOut } ecs_inout_kind_t; /** Specifies whether term identifier is a variable or entity */ typedef enum ecs_var_kind_t { EcsVarDefault, /* Variable if name is all caps, otherwise an entity */ EcsVarIsEntity, /* Term is an entity */ EcsVarIsVariable /* Term is a variable */ } ecs_var_kind_t; /** Type describing an operator used in an signature of a system signature */ typedef enum ecs_oper_kind_t { EcsAnd, /* The term must match */ EcsOr, /* One of the terms in an or chain must match */ EcsNot, /* The term must not match */ EcsOptional, /* The term may match */ EcsAndFrom, /* Term must match all components from term id */ EcsOrFrom, /* Term must match at least one component from term id */ EcsNotFrom /* Term must match none of the components from term id */ } ecs_oper_kind_t; /** Substitution with set parameters. * These parameters allow for substituting a term id with its super- or subsets * for a specified relationship. This enables functionality such as selecting * components from a base (IsA) or a parent (ChildOf) in a single term */ typedef struct ecs_term_set_t { ecs_entity_t relation; /* Relationship to substitute (default = IsA) */ uint8_t mask; /* Substitute as self, subset, superset */ int32_t min_depth; /* Min depth of subset/superset substitution */ int32_t max_depth; /* Max depth of subset/superset substitution */ } ecs_term_set_t; /** Type that describes a single identifier in a term */ typedef struct ecs_term_id_t { ecs_entity_t entity; /* Entity (default = This) */ char *name; /* Name (default = ".") */ ecs_var_kind_t var; /* Is id a variable (default yes if name is * all caps & entity is 0) */ ecs_term_set_t set; /* Set substitution parameters */ } ecs_term_id_t; /** Type that describes a single column in the system signature */ typedef struct ecs_term_t { ecs_id_t id; /* Can be used instead of pred, args and role to * set component/pair id. If not set, it will be * computed from predicate, object. If set, the * subject cannot be set, or be set to This. */ ecs_inout_kind_t inout; /* Access to contents matched with term */ ecs_term_id_t pred; /* Predicate of term */ ecs_term_id_t args[2]; /* Subject (0), object (1) of term */ ecs_oper_kind_t oper; /* Operator of term */ ecs_id_t role; /* Role of term */ char *name; /* Name of term */ int32_t index; /* Computed term index in filter which takes * into account folded OR terms */ bool move; /* When true, this signals to ecs_term_copy that * the resources held by this term may be moved * into the destination term. */ } ecs_term_t; /* Deprecated -- do not use! */ typedef enum ecs_match_kind_t { EcsMatchDefault = 0, EcsMatchAll, EcsMatchAny, EcsMatchExact } ecs_match_kind_t; /** Filters alllow for ad-hoc quick filtering of entity tables. */ struct ecs_filter_t { ecs_term_t *terms; /* Array containing terms for filter */ int32_t term_count; /* Number of elements in terms array */ int32_t term_count_actual; /* Processed count, which folds OR terms */ ecs_term_t term_cache[ECS_TERM_CACHE_SIZE]; /* Cache for small filters */ bool match_this; /* Has terms that match EcsThis */ bool match_only_this; /* Has only terms that match EcsThis */ char *name; /* Name of filter (optional) */ char *expr; /* Expression of filter (if provided) */ /* Deprecated fields -- do not use! */ ecs_type_t include; ecs_type_t exclude; ecs_match_kind_t include_kind; ecs_match_kind_t exclude_kind; }; /** A trigger reacts to events matching a single term */ struct ecs_trigger_t { ecs_term_t term; /* Term describing the trigger condition id */ /* Trigger events */ ecs_entity_t events[ECS_TRIGGER_DESC_EVENT_COUNT_MAX]; int32_t event_count; ecs_iter_action_t action; /* Callback */ void *ctx; /* Callback context */ void *binding_ctx; /* Binding context (for language bindings) */ ecs_ctx_free_t ctx_free; /* Callback to free ctx */ ecs_ctx_free_t binding_ctx_free; /* Callback to free binding_ctx */ ecs_entity_t entity; /* Trigger entity */ ecs_entity_t self; /* Entity associated with observer */ uint64_t id; /* Internal id */ }; /* An observer reacts to events matching a filter */ struct ecs_observer_t { ecs_filter_t filter; /* Triggers created by observer (array size same as number of terms) */ ecs_entity_t *triggers; /* Observer events */ ecs_entity_t events[ECS_TRIGGER_DESC_EVENT_COUNT_MAX]; int32_t event_count; ecs_iter_action_t action; /* Callback */ void *ctx; /* Callback context */ void *binding_ctx; /* Binding context (for language bindings) */ ecs_ctx_free_t ctx_free; /* Callback to free ctx */ ecs_ctx_free_t binding_ctx_free; /* Callback to free binding_ctx */ ecs_entity_t entity; /* Observer entity */ ecs_entity_t self; /* Entity associated with observer */ uint64_t id; /* Internal id */ }; /** @} */ /** * @file api_types.h * @brief Supporting types for the public API. * * This file contains types 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; /* Sparse set */ typedef struct ecs_sparse_t ecs_sparse_t; /* Switch list */ typedef struct ecs_switch_t ecs_switch_t; /* Internal structure to lookup tables for a (component) id */ typedef struct ecs_id_record_t ecs_id_record_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_ids_t { ecs_entity_t *array; /**< An array with entity ids */ int32_t count; /**< The number of entities in the array */ } ecs_ids_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 terms 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_map_iter_t tables; int32_t index; } ecs_scope_iter_t; /** Term-iterator specific data */ typedef struct ecs_term_iter_t { ecs_term_t *term; ecs_id_record_t *self_index; ecs_id_record_t *set_index; ecs_map_iter_t iter; bool iter_set; /* Storage */ ecs_id_t id; int32_t column; ecs_type_t type; ecs_entity_t subject; ecs_size_t size; void *ptr; } ecs_term_iter_t; typedef enum ecs_filter_iter_kind_t { EcsFilterIterEvalIndex, EcsFilterIterEvalNone } ecs_filter_iter_kind_t; /** Filter-iterator specific data */ typedef struct ecs_filter_iter_t { ecs_filter_t filter; ecs_filter_iter_kind_t kind; /* For EcsFilterIterEvalIndex */ ecs_term_iter_t term_iter; int32_t min_term_index; } ecs_filter_iter_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_snapshot_iter_t; /* Inline arrays for queries with small number of components */ typedef struct ecs_iter_cache_t { ecs_id_t ids[ECS_TERM_CACHE_SIZE]; ecs_type_t types[ECS_TERM_CACHE_SIZE]; int32_t columns[ECS_TERM_CACHE_SIZE]; ecs_entity_t subjects[ECS_TERM_CACHE_SIZE]; ecs_size_t sizes[ECS_TERM_CACHE_SIZE]; void *ptrs[ECS_TERM_CACHE_SIZE]; bool ids_alloc; bool types_alloc; bool columns_alloc; bool subjects_alloc; bool sizes_alloc; bool ptrs_alloc; } ecs_iter_cache_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_entity_t event; /**< The event (if applicable) */ ecs_id_t event_id; /**< The (component) id for the event */ ecs_entity_t self; /**< Self entity (if set) */ ecs_table_t *table; /**< Current table */ ecs_data_t *data; ecs_id_t *ids; ecs_type_t *types; int32_t *columns; ecs_entity_t *subjects; ecs_size_t *sizes; void **ptrs; ecs_ref_t *references; 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 */ int32_t term_index; /**< Index of term that triggered an event. * This field will be set to the 'index' field * of a trigger/observer term. */ void *table_columns; /**< Table component data */ ecs_entity_t *entities; /**< Entity identifiers */ void *param; /**< Param passed to ecs_run */ void *ctx; /**< System context */ void *binding_ctx; /**< Binding context */ 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 */ bool is_valid; /**< Set to true after first next() */ ecs_ids_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_term_iter_t term; ecs_filter_iter_t filter; ecs_query_iter_t query; ecs_snapshot_iter_t snapshot; } iter; /**< Iterator specific data */ ecs_iter_cache_t cache; /**< Inline arrays to reduce allocations */ }; 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 //////////////////////////////////////////////////////////////////////////////// typedef struct EcsComponentLifecycle EcsComponentLifecycle; /** 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); /** Copy ctor */ typedef void (*ecs_copy_ctor_t)( ecs_world_t *world, ecs_entity_t component, const EcsComponentLifecycle *callbacks, 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 ctor */ typedef void (*ecs_move_ctor_t)( ecs_world_t *world, ecs_entity_t component, const EcsComponentLifecycle *callbacks, 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); /** Invoked when setting a component */ typedef void (*ecs_on_set_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); #ifdef __cplusplus } #endif #endif /** * @file api_support.h * @brief Support functions and constants. * * 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. */ #ifndef FLECS_API_SUPPORT_H #define FLECS_API_SUPPORT_H #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 */ /** The maximum number of nested function calls before the core will throw a * cycle detected error */ #define ECS_MAX_RECURSION (512) //////////////////////////////////////////////////////////////////////////////// //// 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(EcsIdentifier); /** This allows passing 0 as type to functions that accept types */ #define FLECS__TNULL 0 #define FLECS__T0 0 #define FLECS__E0 0 //////////////////////////////////////////////////////////////////////////////// //// Functions used in declarative (macro) API //////////////////////////////////////////////////////////////////////////////// FLECS_API char* ecs_module_path_from_c( const char *c_name); FLECS_API bool ecs_component_has_actions( const ecs_world_t *world, ecs_entity_t component); FLECS_API void ecs_add_module_tag( ecs_world_t *world, ecs_entity_t module); //////////////////////////////////////////////////////////////////////////////// //// Signature API //////////////////////////////////////////////////////////////////////////////// bool ecs_identifier_is_0( const char *id); bool ecs_identifier_is_var( const char *id); /** Calculate offset from address */ #ifdef __cplusplus #define ECS_OFFSET(o, offset) reinterpret_cast((reinterpret_cast(o)) + (static_cast(offset))) #else #define ECS_OFFSET(o, offset) (void*)(((uintptr_t)(o)) + ((uintptr_t)(offset))) #endif #ifdef __cplusplus } #endif #endif /** * @file type.h * @brief Type API. * * This API contains utilities for working with types. Types are vectors of * component ids, and are used most prominently in the API to construct filters. */ #ifndef FLECS_TYPE_H #define FLECS_TYPE_H #ifdef __cplusplus extern "C" { #endif FLECS_API ecs_type_t ecs_type_from_id( ecs_world_t *world, ecs_entity_t entity); FLECS_API ecs_entity_t ecs_type_to_id( const ecs_world_t *world, ecs_type_t type); FLECS_API char* ecs_type_str( const 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_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_id_t id); FLECS_API ecs_type_t ecs_type_remove( ecs_world_t *world, ecs_type_t type, ecs_id_t id); FLECS_API int32_t ecs_type_index_of( ecs_type_t type, int32_t offset, ecs_id_t id); FLECS_API bool ecs_type_has_id( const ecs_world_t *world, ecs_type_t type, ecs_id_t id, bool owned); FLECS_API int32_t ecs_type_match( const ecs_world_t *world, const ecs_table_t *table, ecs_type_t type, int32_t offset, ecs_id_t id, ecs_entity_t rel, int32_t min_depth, int32_t max_depth, ecs_entity_t *out); FLECS_API bool ecs_type_has_type( const ecs_world_t *world, ecs_type_t type, ecs_type_t has); FLECS_API bool ecs_type_owns_type( const 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); #ifdef __cplusplus } #endif #endif /** * @defgroup desc_types Types used for creating API constructs * @{ */ /** Used with ecs_entity_init */ typedef struct ecs_entity_desc_t { ecs_entity_t entity; /* Optional existing entity handle. */ const char *name; /* Name of the entity. If no entity is provided, an * entity with this name will be looked up first. When * an entity is provided, the name will be verified * with the existing entity. */ const char *sep; /* Optional custom separator for hierarchical names */ const char *root_sep; /* Optional, used for identifiers relative to root */ const char *symbol; /* Optional entity symbol. A symbol is an unscoped * identifier that can be used to lookup an entity. The * primary use case for this is to associate the entity * with a language identifier, such as a type or * function name, where these identifiers differ from * the name they are registered with in flecs. For * example, C type "EcsPosition" might be registered * as "flecs.components.transform.Position", with the * symbol set to "EcsPosition". */ bool use_low_id; /* When set to true, a low id (typically reserved for * components) will be used to create the entity, if * no id is specified. */ /* Array of ids to add to the new or existing entity. */ ecs_id_t add[ECS_MAX_ADD_REMOVE]; /* Array of ids to remove from the existing entity. */ ecs_id_t remove[ECS_MAX_ADD_REMOVE]; /* String expression with components to add */ const char *add_expr; /* String expression with components to remove */ const char *remove_expr; } ecs_entity_desc_t; /** Used with ecs_component_init. */ typedef struct ecs_component_desc_t { ecs_entity_desc_t entity; /* Parameters for component entity */ size_t size; /* Component size */ size_t alignment; /* Component alignment */ } ecs_component_desc_t; /** Used with ecs_type_init. */ typedef struct ecs_type_desc_t { ecs_entity_desc_t entity; /* Parameters for type entity */ ecs_id_t ids[ECS_MAX_ADD_REMOVE]; /* Ids to include in type */ const char *ids_expr; /* Id expression to include in type */ } ecs_type_desc_t; /** Used with ecs_filter_init. */ typedef struct ecs_filter_desc_t { /* Terms of the filter. If a filter has more terms than * ECS_TERM_CACHE_SIZE use terms_buffer */ ecs_term_t terms[ECS_TERM_CACHE_SIZE]; /* For filters with lots of terms an outside array can be provided. */ ecs_term_t *terms_buffer; int32_t terms_buffer_count; /* Substitute IsA relationships by default. If true, any term with 'set' * assigned to DefaultSet will be modified to Self|SuperSet(IsA). */ bool substitute_default; /* Filter expression. Should not be set at the same time as terms array */ const char *expr; /* Optional name of filter, used for debugging. If a filter is created for * a system, the provided name should match the system name. */ const char *name; } ecs_filter_desc_t; /** Used with ecs_query_init. */ typedef struct ecs_query_desc_t { /* Filter for the query */ ecs_filter_desc_t filter; /* Component to be used by order_by */ ecs_entity_t order_by_component; /* Callback used for ordering query results. If order_by_id is 0, the * pointer provided to the callback will be NULL. If the callback is not * set, results will not be ordered. */ ecs_order_by_action_t order_by; /* Id to be used by group_by. This id is passed to the group_by function and * can be used identify the part of an entity type that should be used for * grouping. */ ecs_id_t group_by_id; /* Callback used for grouping results. If the callback is not set, results * will not be grouped. When set, this callback will be used to calculate a * "rank" for each entity (table) based on its components. This rank is then * used to sort entities (tables), so that entities (tables) of the same * rank are "grouped" together when iterated. */ ecs_group_by_action_t group_by; /* Context to pass to group_by */ void *group_by_ctx; /* Function to free group_by_ctx */ ecs_ctx_free_t group_by_ctx_free; /* If set, the query will be created as a subquery. A subquery matches at * most a subset of its parent query. Subqueries do not directly receive * (table) notifications from the world. Instead parent queries forward * results to subqueries. This can improve matching performance, as fewer * queries need to be matched with new tables. * Subqueries can be nested. */ ecs_query_t *parent; /* INTERNAL PROPERTY - system to be associated with query. Do not set, as * this will change in future versions. */ ecs_entity_t system; } ecs_query_desc_t; /** Used with ecs_trigger_init. */ typedef struct ecs_trigger_desc_t { /* Entity to associate with trigger */ ecs_entity_desc_t entity; /* Term specifying the id to subscribe for */ ecs_term_t term; /* Filter expression. May only contain a single term. If this field is set, * the term field is ignored. */ const char *expr; /* Events to trigger on (OnAdd, OnRemove, OnSet, UnSet) */ ecs_entity_t events[ECS_TRIGGER_DESC_EVENT_COUNT_MAX]; /* Callback to invoke on an event */ ecs_iter_action_t callback; /* Associate with entity */ ecs_entity_t self; /* User context to pass to callback */ void *ctx; /* Context to be used for language bindings */ void *binding_ctx; /* Callback to free ctx */ ecs_ctx_free_t ctx_free; /* Callback to free binding_ctx */ ecs_ctx_free_t binding_ctx_free; } ecs_trigger_desc_t; /** Used with ecs_observer_init. */ typedef struct ecs_observer_desc_t { /* Entity to associate with observer */ ecs_entity_desc_t entity; /* Filter for observer */ ecs_filter_desc_t filter; /* Events to observe (OnAdd, OnRemove, OnSet, UnSet) */ ecs_entity_t events[ECS_TRIGGER_DESC_EVENT_COUNT_MAX]; /* Callback to invoke on an event */ ecs_iter_action_t callback; /* Associate with entity */ ecs_entity_t self; /* User context to pass to callback */ void *ctx; /* Context to be used for language bindings */ void *binding_ctx; /* Callback to free ctx */ ecs_ctx_free_t ctx_free; /* Callback to free binding_ctx */ ecs_ctx_free_t binding_ctx_free; } ecs_observer_desc_t; /** @} */ /** * @defgroup builtin_components Builtin components * @{ */ /** A (string) identifier. */ typedef struct EcsIdentifier { char *value; ecs_size_t length; uint64_t hash; } EcsIdentifier; /** 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. */ struct EcsComponentLifecycle { ecs_xtor_t ctor; /* ctor */ ecs_xtor_t dtor; /* dtor */ ecs_copy_t copy; /* copy assignment */ ecs_move_t move; /* move assignment */ void *ctx; /* User defined context */ /* Ctor + copy */ ecs_copy_ctor_t copy_ctor; /* Ctor + move */ ecs_move_ctor_t move_ctor; /* Ctor + move + dtor (or move_ctor + dtor). * This combination is typically used when a component is moved from one * location to a new location, like when it is moved to a new table. If * not set explicitly it will be derived from other callbacks. */ ecs_move_ctor_t ctor_move_dtor; /* Move + dtor. * This combination is typically used when a component is moved from one * location to an existing location, like what happens during a remove. If * not set explicitly it will be derived from other callbacks. */ ecs_move_ctor_t move_dtor; /* Callback that is invoked when an instance of the component is set. This * callback is invoked before triggers are invoked, and enable the component * to respond to changes on itself before others can. */ ecs_on_set_t on_set; }; /** Component that stores reference to trigger */ typedef struct EcsTrigger { const ecs_trigger_t *trigger; } EcsTrigger; /** Component that stores reference to observer */ typedef struct EcsObserver { const ecs_observer_t *observer; } EcsObserver; /** Component for storing a query */ typedef struct EcsQuery { ecs_query_t *query; } EcsQuery; /** @} */ /** * @defgroup misc_types Miscalleneous types * @{ */ /** 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) */ 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; /** @} */ /* Only include deprecated definitions if deprecated addon is required */ #ifdef FLECS_DEPRECATED /** * @file deprecated.h * @brief The deprecated addon contains deprecated operations. */ #ifdef FLECS_DEPRECATED #ifndef FLECS_DEPRECATED_H #define FLECS_DEPRECATED_H #ifdef __cplusplus extern "C" { #endif #define ecs_typeid(T) FLECS__E##T #define ecs_entity(T) ecs_typeid(T) #define ecs_add_trait(world, entity, component, trait)\ ecs_add_entity(world, entity, ecs_trait(component, trait)) #define ecs_remove_trait(world, entity, component, trait)\ ecs_remove_entity(world, entity, ecs_trait(component, trait)) #define ecs_has_trait(world, entity, component, trait)\ ecs_has_entity(world, entity, ecs_trait(component, trait)) #ifndef FLECS_LEGACY #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__) #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 #define ecs_get_trait(world, entity, component, trait)\ ((trait*)ecs_get_id(world, entity, ecs_trait(ecs_typeid(component), ecs_typeid(trait)))) #define ecs_get_trait_tag(world, entity, trait, component)\ ((component*)ecs_get_id(world, entity, ecs_trait(ecs_typeid(component), trait))) #define ECS_PREFAB(world, id, ...) \ ecs_entity_t id = ecs_entity_init(world, &(ecs_entity_desc_t){\ .name = #id,\ .add_expr = #__VA_ARGS__,\ .add = {EcsPrefab}\ });\ (void)id #define ECS_ENTITY_EXTERN(id)\ extern ecs_entity_t id #define ECS_ENTITY_DECLARE(id)\ ecs_entity_t id #define ECS_ENTITY_DEFINE(world, id, ...)\ id = ecs_entity_init(world, &(ecs_entity_desc_t){\ .name = #id,\ .add_expr = #__VA_ARGS__\ });\ #define ECS_ENTITY(world, id, ...)\ ecs_entity_t id = ecs_entity_init(world, &(ecs_entity_desc_t){\ .name = #id,\ .add_expr = #__VA_ARGS__\ });\ (void)id #define ECS_COMPONENT(world, id) \ ecs_id_t ecs_id(id) = ecs_component_init(world, &(ecs_component_desc_t){\ .entity = {\ .name = #id,\ .symbol = #id\ },\ .size = sizeof(id),\ .alignment = ECS_ALIGNOF(id)\ });\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &FLECS__E##id, 1);\ (void)ecs_id(id);\ (void)ecs_type(id) #define ECS_COMPONENT_EXTERN(id)\ extern ecs_id_t ecs_id(id);\ extern ecs_type_t ecs_type(id) #define ECS_COMPONENT_DECLARE(id)\ ecs_id_t ecs_id(id);\ ecs_type_t ecs_type(id) #define ECS_COMPONENT_DEFINE(world, id)\ ecs_id(id) = ecs_component_init(world, &(ecs_component_desc_t){\ .entity = {\ .entity = ecs_id(id),\ .name = #id,\ .symbol = #id\ },\ .size = sizeof(id),\ .alignment = ECS_ALIGNOF(id)\ });\ ecs_type(id) = ecs_type_from_entity(world, ecs_id(id)) #define ECS_TAG(world, id)\ ecs_entity_t id = ecs_entity_init(world, &(ecs_entity_desc_t){\ .name = #id,\ .symbol = #id\ });\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &id, 1);\ (void)ecs_type(id) #define ECS_TAG_EXTERN(id)\ extern ecs_entity_t id;\ extern ecs_type_t ecs_type(id) #define ECS_TAG_DECLARE(id)\ ecs_entity_t id;\ ecs_type_t ecs_type(id) #define ECS_TAG_DEFINE(world, id)\ id = ecs_entity_init(world, &(ecs_entity_desc_t){\ .name = #id,\ .symbol = #id\ });\ ecs_type(id) = ecs_type_from_entity(world, id) #define ECS_TYPE(world, id, ...) \ ecs_entity_t id = ecs_type_init(world, &(ecs_type_desc_t){\ .entity.name = #id,\ .ids_expr = #__VA_ARGS__\ });\ ecs_type_t ecs_type(id) = ecs_type_from_entity(world, id);\ (void)id;\ (void)ecs_type(id) #define ECS_TYPE_EXTERN(id)\ extern ecs_entity_t id;\ extern ecs_type_t ecs_type(id) #define ECS_TYPE_DECLARE(id)\ ecs_entity_t id;\ ecs_type_t ecs_type(id) #define ECS_TYPE_DEFINE(world, id, ...)\ id = ecs_type_init(world, &(ecs_type_desc_t){\ .entity.name = #id,\ .ids_expr = #__VA_ARGS__\ });\ ecs_type(id) = ecs_type_from_entity(world, id);\ #define ECS_COLUMN(it, type, id, column)\ ecs_id_t ecs_id(type) = ecs_column_entity(it, column);\ ecs_type_t ecs_type(type) = ecs_column_type(it, column);\ type *id = ecs_column(it, type, column);\ (void)ecs_id(type);\ (void)ecs_type(type);\ (void)id #define ECS_COLUMN_COMPONENT(it, id, column)\ ecs_id_t ecs_id(id) = ecs_column_entity(it, column);\ ecs_type_t ecs_type(id) = ecs_column_type(it, column);\ (void)ecs_id(id);\ (void)ecs_type(id) #define ECS_COLUMN_ENTITY(it, id, column)\ ecs_entity_t id = ecs_column_entity(it, column);\ ecs_type_t ecs_type(id) = ecs_column_type(it, column);\ (void)id;\ (void)ecs_type(id) #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)) #define ecs_new(world, type) ecs_new_w_type(world, ecs_type(type)) #define ecs_bulk_new(world, component, count)\ ecs_bulk_new_w_type(world, ecs_type(component), count) #define ecs_add(world, entity, component)\ ecs_add_type(world, entity, ecs_type(component)) #define ecs_remove(world, entity, type)\ ecs_remove_type(world, entity, ecs_type(type)) #define ecs_add_remove(world, entity, to_add, to_remove)\ ecs_add_remove_type(world, entity, ecs_type(to_add), ecs_type(to_remove)) #define ecs_has(world, entity, type)\ ecs_has_type(world, entity, ecs_type(type)) #define ecs_owns(world, entity, type, owned)\ ecs_type_owns_type(world, ecs_get_type(world, entity), ecs_type(type), owned) #define ecs_set_ptr_w_id(world, entity, size, ptr)\ ecs_set_id(world, entity, size, ptr) #define ecs_owns_entity(world, entity, id, owned)\ ecs_type_has_id(world, ecs_get_type(world, entity), id, owned) typedef ecs_ids_t ecs_entities_t; ECS_DEPRECATED("deprecated functionality") FLECS_API void ecs_dim_type( ecs_world_t *world, ecs_type_t type, int32_t entity_count); ECS_DEPRECATED("use ecs_new_w_id") FLECS_API ecs_entity_t ecs_new_w_type( ecs_world_t *world, ecs_type_t type); ECS_DEPRECATED("use ecs_bulk_new_w_id") FLECS_API const ecs_entity_t* ecs_bulk_new_w_type( ecs_world_t *world, ecs_type_t type, int32_t count); ECS_DEPRECATED("use ecs_add_id") FLECS_API void ecs_add_type( ecs_world_t *world, ecs_entity_t entity, ecs_type_t type); ECS_DEPRECATED("use ecs_remove_id") FLECS_API void ecs_remove_type( ecs_world_t *world, ecs_entity_t entity, ecs_type_t type); ECS_DEPRECATED("use ecs_add_remove_id") 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); ECS_DEPRECATED("use ecs_has_id") FLECS_API bool ecs_has_type( const ecs_world_t *world, ecs_entity_t entity, ecs_type_t type); ECS_DEPRECATED("use ecs_count_filter") FLECS_API int32_t ecs_count_type( const ecs_world_t *world, ecs_type_t type); ECS_DEPRECATED("use ecs_count_id") FLECS_API int32_t ecs_count_entity( const ecs_world_t *world, ecs_id_t entity); ECS_DEPRECATED("use ecs_count_filter") FLECS_API int32_t ecs_count_w_filter( const ecs_world_t *world, const ecs_filter_t *filter); ECS_DEPRECATED("use ecs_set_component_actions_w_entity") FLECS_API void ecs_set_component_actions_w_entity( ecs_world_t *world, ecs_id_t id, EcsComponentLifecycle *actions); ECS_DEPRECATED("use ecs_new_w_id") FLECS_API ecs_entity_t ecs_new_w_entity( ecs_world_t *world, ecs_id_t id); ECS_DEPRECATED("use ecs_bulk_new_w_id") FLECS_API const ecs_entity_t* ecs_bulk_new_w_entity( ecs_world_t *world, ecs_id_t id, int32_t count); ECS_DEPRECATED("use ecs_enable_component_w_id") FLECS_API void ecs_enable_component_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, bool enable); ECS_DEPRECATED("use ecs_is_component_enabled_w_id") FLECS_API bool ecs_is_component_enabled_w_entity( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_get_id") FLECS_API const void* ecs_get_w_id( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_get_id") FLECS_API const void* ecs_get_w_entity( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_get_ref_w_id") FLECS_API const void* ecs_get_ref_w_entity( const ecs_world_t *world, ecs_ref_t *ref, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_get_mut_id") FLECS_API void* ecs_get_mut_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, bool *is_added); ECS_DEPRECATED("use ecs_get_mut_id") FLECS_API void* ecs_get_mut_w_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, bool *is_added); ECS_DEPRECATED("use ecs_modified_id") FLECS_API void ecs_modified_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_modified_id") void ecs_modified_w_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_set_id") FLECS_API ecs_entity_t ecs_set_ptr_w_entity( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, size_t size, const void *ptr); ECS_DEPRECATED("use ecs_has_id") FLECS_API bool ecs_has_entity( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_id_str") FLECS_API size_t ecs_entity_str( const ecs_world_t *world, ecs_id_t entity, char *buffer, size_t buffer_len); ECS_DEPRECATED("use ecs_get_object_w_id(world, entity, EcsChildOf, id)") FLECS_API ecs_entity_t ecs_get_parent_w_entity( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); #define ecs_get_parent(world, entity, component)\ ecs_get_parent_w_entity(world, entity, ecs_typeid(component)) ECS_DEPRECATED("use ecs_get_stage_id") FLECS_API int32_t ecs_get_thread_index( const ecs_world_t *world); ECS_DEPRECATED("use ecs_add_id") FLECS_API void ecs_add_entity( ecs_world_t *world, ecs_entity_t entity, ecs_entity_t entity_add); ECS_DEPRECATED("use ecs_remove_id") FLECS_API void ecs_remove_entity( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); ECS_DEPRECATED("use ecs_add_id / ecs_remove_id") FLECS_API void ecs_add_remove_entity( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id_add, ecs_id_t id_remove); ECS_DEPRECATED("use ecs_type_from_id") FLECS_API ecs_type_t ecs_type_from_entity( ecs_world_t *world, ecs_entity_t entity); ECS_DEPRECATED("use ecs_type_to_id") FLECS_API ecs_entity_t ecs_type_to_entity( const ecs_world_t *world, ecs_type_t type); ECS_DEPRECATED("use ecs_type_has_id") FLECS_API bool ecs_type_has_entity( const ecs_world_t *world, ecs_type_t type, ecs_entity_t entity); ECS_DEPRECATED("use ecs_type_has_id") FLECS_API bool ecs_type_owns_entity( const ecs_world_t *world, ecs_type_t type, ecs_entity_t entity, bool owned); ECS_DEPRECATED("use ecs_term/ecs_term_w_size") FLECS_API void* ecs_column_w_size( const ecs_iter_t *it, size_t size, int32_t column); #define ecs_column(it, T, column)\ ecs_column_w_size(it, sizeof(T), column) ECS_DEPRECATED("no replacement") FLECS_API int32_t ecs_column_index_from_name( const ecs_iter_t *it, const char *name); ECS_DEPRECATED("use ecs_term_source") FLECS_API ecs_entity_t ecs_column_source( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("use ecs_term_id") FLECS_API ecs_entity_t ecs_column_entity( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("no replacement") FLECS_API ecs_type_t ecs_column_type( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("use ecs_term_size") FLECS_API size_t ecs_column_size( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("use ecs_term_is_readonly") FLECS_API bool ecs_is_readonly( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("use ecs_term_is_owned") FLECS_API bool ecs_is_owned( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("use ecs_iter_column") FLECS_API void* ecs_table_column( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("use ecs_iter_column_size") FLECS_API size_t ecs_table_column_size( const ecs_iter_t *it, int32_t column); ECS_DEPRECATED("use ecs_iter_column_index") FLECS_API int32_t ecs_table_component_index( const ecs_iter_t *it, ecs_entity_t component); ECS_DEPRECATED("use ecs_set_rate") FLECS_API ecs_entity_t ecs_set_rate_filter( ecs_world_t *world, ecs_entity_t filter, int32_t rate, ecs_entity_t source); ECS_DEPRECATED("use ecs_query_init") FLECS_API ecs_query_t* ecs_query_new( ecs_world_t *world, const char *sig); ECS_DEPRECATED("use ecs_query_init") FLECS_API ecs_query_t* ecs_subquery_new( ecs_world_t *world, ecs_query_t *parent, const char *sig); ECS_DEPRECATED("use ecs_query_deinit") FLECS_API void ecs_query_free( ecs_query_t *query); ECS_DEPRECATED("use ecs_query_init") FLECS_API void ecs_query_order_by( ecs_world_t *world, ecs_query_t *query, ecs_entity_t component, ecs_order_by_action_t compare); ECS_DEPRECATED("use ecs_query_init") FLECS_API void ecs_query_group_by( ecs_world_t *world, ecs_query_t *query, ecs_entity_t component, ecs_group_by_action_t rank_action); #ifdef __cplusplus } #endif #endif #endif #endif /** * @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_pair(world, e, EcsChildOf, parent); * * Type flags can also be used in type expressions, without the ECS prefix: * * ECS_ENTITY(world, Base, Position); * ECS_TYPE(world, InstanceOfBase, (IsA, Base)); */ /** Role bit added to roles to differentiate between roles and generations */ #define ECS_ROLE (1ull << 63) /** Cases are used to switch between mutually exclusive components */ FLECS_API extern const ecs_id_t ECS_CASE; /** Switches allow for fast switching between mutually exclusive components */ FLECS_API extern const ecs_id_t ECS_SWITCH; /** The PAIR role indicates that the entity is a pair identifier. */ FLECS_API extern const ecs_id_t ECS_PAIR; /** Enforce ownership of a component */ FLECS_API extern const ecs_id_t ECS_OWNED; /** Track whether component is enabled or not */ FLECS_API extern const ecs_id_t ECS_DISABLED; /** @} */ /** * @defgroup builtin_tags Builtin Tags * @{ */ /** Root scope for builtin flecs entities */ FLECS_API extern const ecs_entity_t EcsFlecs; /* Core module scope */ FLECS_API extern const ecs_entity_t EcsFlecsCore; /* Entity associated with world (used for "attaching" components to world) */ FLECS_API extern const ecs_entity_t EcsWorld; /* Wildcard entity ("*"), Used in expressions to indicate wildcard matching */ FLECS_API extern const ecs_entity_t EcsWildcard; /* This entity (".", "This"). Used in expressions to indicate This entity */ FLECS_API extern const ecs_entity_t EcsThis; /* Can be added to relation to indicate it is transitive. */ FLECS_API extern const ecs_entity_t EcsTransitive; /* Can be added to component/relation to indicate it is final. Final components/ * relations cannot be derived from using an IsA relationship. Queries will not * attempt to substitute a component/relationship with IsA subsets if they are * final. */ FLECS_API extern const ecs_entity_t EcsFinal; /* Can be added to relation to indicate that it should never hold data, even * when it or the relation object is a component. */ FLECS_API extern const ecs_entity_t EcsTag; /* Tag to indicate name identifier */ FLECS_API extern const ecs_entity_t EcsName; /* Tag to indicate symbol identifier */ FLECS_API extern const ecs_entity_t EcsSymbol; /* Used to express parent-child relations. */ FLECS_API extern const ecs_entity_t EcsChildOf; /* Used to express is-a relations. An IsA relation indicates that the subject is * a subset of the relation object. For example: * ecs_add_pair(world, Freighter, EcsIsA, SpaceShip); * * Here the Freighter is considered a subset of SpaceShip, meaning that every * entity that has Freighter also implicitly has SpaceShip. * * The subject of the relation (Freighter) inherits all components from any IsA * object (SpaceShip). If SpaceShip has a component "MaxSpeed", this component * will also appear on Freighter after adding (IsA, SpaceShip) to Freighter. * * The IsA relation is transitive. This means that if SpaceShip IsA Machine, * then Freigther is also a Machine. As a result, Freighter also inherits all * components from Machine, just as it does from SpaceShip. * * Queries/filters may implicitly substitute predicates, subjects and objects * with their IsA super/subsets. This behavior can be controlled by the "set" * member of a query term. */ FLECS_API extern const ecs_entity_t EcsIsA; /* Tag added to module entities */ FLECS_API extern const ecs_entity_t EcsModule; /* Tag added to prefab entities. Any entity with this tag is automatically * ignored by filters/queries, unless EcsPrefab is explicitly added. */ FLECS_API extern const ecs_entity_t EcsPrefab; /* When this tag is added to an entity it is skipped by all queries/filters */ FLECS_API extern const ecs_entity_t EcsDisabled; /* Tag added to builtin/framework entites. This tag can be used to automatically * hide components/systems that are part of infrastructure code vs. application * code. The tag has no functional implications. */ FLECS_API extern const ecs_entity_t EcsHidden; /* Event. Triggers when an id (component, tag, pair) is added to an entity */ FLECS_API extern const ecs_entity_t EcsOnAdd; /* Event. Triggers when an id (component, tag, pair) is removed from an entity */ FLECS_API extern const ecs_entity_t EcsOnRemove; /* Event. Triggers when a component is set for an entity */ FLECS_API extern const ecs_entity_t EcsOnSet; /* Event. Triggers when a component is unset for an entity */ FLECS_API extern const ecs_entity_t EcsUnSet; /* Event. Triggers when an entity is deleted. * Also used as relation for defining cleanup behavior, see: * https://github.com/SanderMertens/flecs/blob/master/docs/Relations.md#relation-cleanup-properties */ FLECS_API extern const ecs_entity_t EcsOnDelete; /* Event. Triggers when a table is created. */ // FLECS_API extern const ecs_entity_t EcsOnCreateTable; /* Event. Triggers when a table is deleted. */ // FLECS_API extern const ecs_entity_t EcsOnDeleteTable; /* Event. Triggers when a table becomes empty (doesn't trigger on creation). */ // FLECS_API extern const ecs_entity_t EcsOnTableEmpty; /* Event. Triggers when a table becomes non-empty. */ // FLECS_API extern const ecs_entity_t EcsOnTableNonEmpty; /* Event. Triggers when a trigger is created. */ // FLECS_API extern const ecs_entity_t EcsOnCreateTrigger; /* Event. Triggers when a trigger is deleted. */ // FLECS_API extern const ecs_entity_t EcsOnDeleteTrigger; /* Event. Triggers when observable is deleted. */ // FLECS_API extern const ecs_entity_t EcsOnDeleteObservable; /* Event. Triggers when lifecycle methods for a component are registered */ // FLECS_API extern const ecs_entity_t EcsOnComponentLifecycle; /* Relationship used to define what should happen when an entity is deleted that * is added to other entities. For details see: * https://github.com/SanderMertens/flecs/blob/master/docs/Relations.md#relation-cleanup-properties */ FLECS_API extern const ecs_entity_t EcsOnDeleteObject; /* Specifies that a component/relation/object of relation should be removed when * it is deleted. Must be combined with EcsOnDelete or EcsOnDeleteObject. */ FLECS_API extern const ecs_entity_t EcsRemove; /* Specifies that entities with a component/relation/object of relation should * be deleted when the component/relation/object of relation is deleted. Must be * combined with EcsOnDelete or EcsOnDeleteObject. */ FLECS_API extern const ecs_entity_t EcsDelete; /* Specifies that whenever a component/relation/object of relation is deleted an * error should be thrown. Must be combined with EcsOnDelete or * EcsOnDeleteObject. */ FLECS_API extern const ecs_entity_t EcsThrow; /* System module tags */ FLECS_API extern const ecs_entity_t EcsOnDemand; FLECS_API extern const ecs_entity_t EcsMonitor; FLECS_API extern const ecs_entity_t EcsDisabledIntern; FLECS_API extern const ecs_entity_t EcsInactive; /* Pipeline module tags */ FLECS_API extern const ecs_entity_t EcsPipeline; FLECS_API extern const ecs_entity_t EcsPreFrame; FLECS_API extern const ecs_entity_t EcsOnLoad; FLECS_API extern const ecs_entity_t EcsPostLoad; FLECS_API extern const ecs_entity_t EcsPreUpdate; FLECS_API extern const ecs_entity_t EcsOnUpdate; FLECS_API extern const ecs_entity_t EcsOnValidate; FLECS_API extern const ecs_entity_t EcsPostUpdate; FLECS_API extern const ecs_entity_t EcsPreStore; FLECS_API extern const ecs_entity_t EcsOnStore; FLECS_API extern const ecs_entity_t EcsPostFrame; /* 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_id(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 + 128) /** @} */ /** * @defgroup convenience_macros Convenience Macro's * @{ */ /* Macro's rely on variadic arguments which are C99 and above */ #ifndef FLECS_LEGACY /** Declare a component. * Example: * ECS_COMPONENT(world, Position); */ #ifndef ECS_COMPONENT #define ECS_COMPONENT(world, id) \ ecs_id_t ecs_id(id) = ecs_component_init(world, &(ecs_component_desc_t){\ .entity = {\ .name = #id,\ .symbol = #id\ },\ .size = sizeof(id),\ .alignment = ECS_ALIGNOF(id)\ });\ (void)ecs_id(id); #endif /** 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); */ #ifndef ECS_COMPONENT_EXTERN #define ECS_COMPONENT_EXTERN(id)\ extern ecs_id_t ecs_id(id); #endif /** 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); */ #ifndef ECS_COMPONENT_DECLARE #define ECS_COMPONENT_DECLARE(id)\ ecs_id_t ecs_id(id); #endif /** 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); */ #ifndef ECS_COMPONENT_DEFINE #define ECS_COMPONENT_DEFINE(world, id)\ ecs_id(id)= ecs_component_init(world, &(ecs_component_desc_t){\ .entity = {\ .entity = ecs_id(id),\ .name = #id,\ .symbol = #id\ },\ .size = sizeof(id),\ .alignment = ECS_ALIGNOF(id)\ }); #endif /** Declare a tag. * Example: * ECS_TAG(world, MyTag); */ #ifndef ECS_TAG #define ECS_TAG(world, id)\ ECS_ENTITY(world, id, 0); #endif /** 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); */ #ifndef ECS_TAG_EXTERN #define ECS_TAG_EXTERN(id)\ extern ecs_entity_t id; #endif /** 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); */ #ifndef ECS_TAG_DECLARE #define ECS_TAG_DECLARE(id)\ ecs_entity_t id; #endif /** 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); */ #ifndef ECS_TAG_DEFINE #define ECS_TAG_DEFINE(world, id)\ id = ecs_entity_init(world, &(ecs_entity_desc_t){\ .name = #id\ }); #endif /** 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__) /** Declare an on_set action. * Example: * ECS_ON_SET(MyType, ptr, { printf("%d\n", ptr->value); }); */ #define ECS_ON_SET(type, ptr, ...)\ ECS_ON_SET_IMPL(type, ptr, __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 #define ecs_on_set(type) type##_on_set #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); /** 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); /** Return whether a quit has been signaled. * * @param world The world. */ FLECS_API bool ecs_should_quit( const ecs_world_t *world); /** 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_id( ecs_world_t *world, ecs_id_t id, EcsComponentLifecycle *actions); #ifndef FLECS_LEGACY #define ecs_set_component_actions(world, component, ...)\ ecs_set_component_actions_w_id(world, ecs_id(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( const 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( const 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); /** 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); /** 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); /** 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); /** Get current number of threads. */ FLECS_API int32_t ecs_get_threads( ecs_world_t *world); /** @} */ /** * @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_id( ecs_world_t *world, ecs_id_t id); /** 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 component The component. * @return The new entity. */ #ifndef ecs_new #define ecs_new(world, type) ecs_new_w_id(world, ecs_id(type)) #endif /** Find or create an entity. * This operation creates a new entity, or modifies an existing one. When a name * is set in the ecs_entity_desc_t::name field and ecs_entity_desc_t::entity is * not set, the operation will first attempt to find an existing entity by that * name. If no entity with that name can be found, it will be created. * * If both a name and entity handle are provided, the operation will check if * the entity name matches with the provided name. If the names do not match, * the function will fail and return 0. * * If an id to a non-existing entity is provided, that entity id become alive. * * See the documentation of ecs_entity_desc_t for more details. * * @param world The world. * @param desc Entity init parameters. * @return A handle to the new or existing entity, or 0 if failed. */ FLECS_API ecs_entity_t ecs_entity_init( ecs_world_t *world, const ecs_entity_desc_t *desc); /** Find or create a component. * This operation creates a new component, or finds an existing one. The find or * create behavior is the same as ecs_entity_init. * * When an existing component is found, the size and alignment are verified with * the provided values. If the values do not match, the operation will fail. * * See the documentation of ecs_component_desc_t for more details. * * @param world The world. * @param desc Component init parameters. * @return A handle to the new or existing component, or 0 if failed. */ FLECS_API ecs_entity_t ecs_component_init( ecs_world_t *world, const ecs_component_desc_t *desc); /** Create a new type entity. * This operation creates a new type entity, or finds an existing one. The find * or create behavior is the same as ecs_entity_init. * * A type entity is an entity with the EcsType component. This component * a pointer to an ecs_type_t, which allows for the creation of named types. * Named types are used in a few places, such as for pipelines and filter terms * with the EcsAndFrom or EcsOrFrom operators. * * When an existing type entity is found, its types are verified with the * provided values. If the values do not match, the operation will fail. * * See the documentation of ecs_type_desc_t for more details. * * @param world The world. * @param desc Type entity init parameters. * @return A handle to the new or existing type, or 0 if failed. */ FLECS_API ecs_entity_t ecs_type_init( ecs_world_t *world, const ecs_type_desc_t *desc); /** Create N new entities. * This operation is the same as ecs_new_w_id, 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_id( ecs_world_t *world, ecs_id_t id, 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, const ecs_ids_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. */ #ifndef ecs_bulk_new #define ecs_bulk_new(world, component, count)\ ecs_bulk_new_w_id(world, ecs_id(component), count) #endif /** 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 id The id to add. */ FLECS_API void ecs_add_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); /** 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. */ #ifndef ecs_add #define ecs_add(world, entity, component)\ ecs_add_id(world, entity, ecs_id(component)) #endif /** 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 id The id to remove. */ FLECS_API void ecs_remove_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); /** 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. */ #ifndef ecs_remove #define ecs_remove(world, entity, type)\ ecs_remove_id(world, entity, ecs_id(type)) #endif /** @} */ /** * @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 id The component. * @param enable True to enable the component, false to disable. */ FLECS_API void ecs_enable_component_w_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, bool enable); #define ecs_enable_component(world, entity, T, enable)\ ecs_enable_component_w_id(world, entity, ecs_id(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 id The component. * @return True if the component is enabled, otherwise false. */ FLECS_API bool ecs_is_component_enabled_w_id( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); #define ecs_is_component_enabled(world, entity, T)\ ecs_is_component_enabled_w_id(world, entity, ecs_id(T)) /** @} */ /** * @defgroup pairs Pairs * @{ */ /** Make a pair identifier. * This function is equivalent to using the ecs_pair macro, and is added for * convenience to make it easier for non C/C++ bindings to work with pairs. * * @param relation The relation of the pair. * @param object The object of the pair. */ FLECS_API ecs_id_t ecs_make_pair( ecs_entity_t relation, ecs_entity_t object); /** This operation accepts regular entities. For passing in component identifiers * use ecs_typeid, like this: * * ecs_new_w_pair(world, ecs_id(relation), object) * * @param world The world. * @param relation The relation part of the pair to add. * @param object The object part of the pair to add. * @return The new entity. */ #define ecs_new_w_pair(world, relation, object)\ ecs_new_w_id(world, ecs_pair(relation, object)) /** Add a pair. * This operation adds a pair to an entity. A pair is a combination of a * relation and an object, can can be used to store relationships between * entities. Example: * * subject = Alice, relation = Likes, object = Bob * * This operation accepts regular entities. For passing in component identifiers * use ecs_typeid, like this: * * ecs_add_pair(world, subject, ecs_id(relation), object) * * @param world The world. * @param subject The entity to which to add the pair. * @param relation The relation part of the pair to add. * @param object The object part of the pair to add. */ #define ecs_add_pair(world, subject, relation, object)\ ecs_add_id(world, subject, ecs_pair(relation, object)) /** Remove a pair. * This operation removes a pair from an entity. A pair is a combination of a * relation and an object, can can be used to store relationships between * entities. Example: * * subject = Alice, relation = Likes, object = Bob * * This operation accepts regular entities. For passing in component identifiers * use ecs_typeid, like this: * * ecs_remove_pair(world, subject, ecs_id(relation), object) * * @param world The world. * @param subject The entity from which to remove the pair. * @param relation The relation part of the pair to remove. * @param object The object part of the pair to remove. */ #define ecs_remove_pair(world, subject, relation, object)\ ecs_remove_id(world, subject, ecs_pair(relation, object)) /** Test for a pair. * This operation tests if an entity has a pair. This operation accepts regular * entities. For passing in component identifiers use ecs_typeid, like this: * * ecs_has_pair(world, subject, ecs_id(relation), object) * * @param world The world. * @param subject The entity from which to remove the pair. * @param relation The relation part of the pair to remove. * @param object The object part of the pair to remove. */ #define ecs_has_pair(world, subject, relation, object)\ ecs_has_id(world, subject, ecs_pair(relation, object)) #ifndef FLECS_LEGACY /** Set relation of pair. * This operation sets data for a pair, where the relation determines the type. * A pair is a combination of a relation and an object, can can be used to store * relationships between entities. * * Pairs can contain data if either the relation or object of the pair are a * component. If both are a component, the relation takes precedence. * * If this operation is used with a pair where the relation is not a component, * it will fail. The object part of the pair expects a regular entity. To pass * a component as object, use ecs_typeid like this: * * ecs_set_pair(world, subject, relation, ecs_id(object)) * * @param world The world. * @param subject The entity on which to set the pair. * @param relation The relation part of the pair. This must be a component. * @param object The object part of the pair. */ #define ecs_set_pair(world, subject, relation, object, ...)\ ecs_set_id(world, subject,\ ecs_pair(ecs_id(relation), object),\ sizeof(relation), &(relation)__VA_ARGS__) /** Set object of pair. * This operation sets data for a pair, where the object determines the type. * A pair is a combination of a relation and an object, can can be used to store * relationships between entities. * * Pairs can contain data if either the relation or object of the pair are a * component. If both are a component, the relation takes precedence. * * If this operation is used with a pair where the object is not a component, * it will fail. The relation part of the pair expects a regular entity. To pass * a component as relation, use ecs_typeid like this: * * ecs_set_pair_object(world, subject, ecs_id(relation), object) * * @param world The world. * @param subject The entity. * @param relation The relation part of the pair. * @param object The object part of the pair. This must be a component. */ #define ecs_set_pair_object(world, subject, relation, object, ...)\ ecs_set_id(world, subject,\ ecs_pair(relation, ecs_id(object)),\ sizeof(object), &(object)__VA_ARGS__) #define ecs_get_mut_pair(world, subject, relation, object, is_added)\ (ECS_CAST(relation*, ecs_get_mut_id(world, subject,\ ecs_pair(ecs_id(relation), object), is_added))) #define ecs_get_mut_pair_object(world, subject, relation, object, is_added)\ (ECS_CAST(object*, ecs_get_mut_id(world, subject,\ ecs_pair(relation, ecs_id(object)), is_added))) #define ecs_modified_pair(world, subject, relation, object)\ ecs_modified_id(world, subject, ecs_pair(relation, object)) #endif /** Get relation of pair. * This operation obtains the value of a pair, where the relation determines the * type. A pair is a combination of a relation and an object, can can be used to * store relationships between entities. * * Pairs can contain data if either the relation or object of the pair are a * component. If both are a component, the relation takes precedence. * * If this operation is used with a pair where the relation is not a component, * it will fail. The object part of the pair expects a regular entity. To pass * a component as relation, use ecs_typeid like this: * * ecs_get_pair(world, subject, relation, ecs_id(object)) * * @param world The world. * @param subject The entity. * @param relation The relation part of the pair. Must be a component. * @param object The object part of the pair. */ #define ecs_get_pair(world, subject, relation, object)\ (ECS_CAST(relation*, ecs_get_id(world, subject,\ ecs_pair(ecs_id(relation), object)))) /** Get object of pair. * This operation obtains the value of a pair, where the object determines the * type. A pair is a combination of a relation and an object, can can be used to * store relationships between entities. * * Pairs can contain data if either the relation or object of the pair are a * component. If both are a component, the relation takes precedence. * * If this operation is used with a pair where the object is not a component, * it will fail. The relation part of the pair expects a regular entity. To pass * a component as relation, use ecs_typeid like this: * * ecs_get_pair_object(world, subject, ecs_id(relation), object) * * @param world The world. * @param subject The entity. * @param relation The relation part of the pair. Must be a component. * @param object The object part of the pair. */ #define ecs_get_pair_object(world, subject, relation, object)\ (ECS_CAST(object*, ecs_get_id(world, subject,\ ecs_pair(relation, ecs_id(object))))) /** @} */ /** * @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_id 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_id( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); /** Get an immutable pointer to a component. * Same as ecs_get_id, but accepts the typename of a component. * * @param world The world. * @param entity The entity. * @param id The component to obtain. * @return The component pointer, NULL if the entity does not have the component. */ #define ecs_get(world, entity, component)\ (ECS_CAST(const component*, ecs_get_id(world, entity, ecs_id(component)))) /* -- Get cached pointer -- */ /** Get an immutable reference to a component. * This operation is similar to ecs_get_id 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_id( const ecs_world_t *world, ecs_ref_t *ref, ecs_entity_t entity, ecs_id_t id); /** Get an immutable reference to a component. * Same as ecs_get_ref_w_id, 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 id 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)\ (ECS_CAST(const component*, ecs_get_ref_w_id(world, ref, entity, ecs_id(component)))) /** 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( const ecs_world_t *world, ecs_entity_t e, ecs_entity_t sw); /** @} */ /** * @defgroup setting Setting Components * @{ */ /** Get a mutable pointer to a component. * This operation is similar to ecs_get_id 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 id 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_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, bool *is_added); /** Get a mutable pointer to a component. * Same as ecs_get_mut_id but accepts a component typename. * * @param world The world. * @param entity The entity. * @param T The component type 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, T, is_added)\ (ECS_CAST(T*, ecs_get_mut_id(world, entity, ecs_id(T), is_added))) /** Emplace a component. * Emplace is similar to get_mut except that the component constructor is not * invoked for the returned pointer, allowing the component to be "constructed" * directly in the storage. * * Emplace can only be used if the entity does not yet have the component. If * the entity has the component, the operation will fail. * * @param world The world. * @param entity The entity. * @param id The component to obtain. * @return The (uninitialized) component pointer. */ FLECS_API void* ecs_emplace_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); /** Emplace a component. * Same as ecs_emplace_id but accepts a typename. * * @param world The world. * @param entity The entity. * @param id The component to obtain. * @return The (uninitialized) component pointer. */ #define ecs_emplace(world, entity, T)\ (ECS_CAST(T*, ecs_emplace_id(world, entity, ecs_id(T)))) /** 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_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); /** Signal that a component has been modified. * Same as ecs_modified_id but accepts a component typename. * * @param world The world. * @param entity The entity. * @param id The component that was modified. */ #define ecs_modified(world, entity, component)\ ecs_modified_id(world, entity, ecs_id(component)) /** 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_id( ecs_world_t *world, ecs_entity_t entity, ecs_id_t id, size_t size, const void *ptr); /** Set the value of a component. * Same as ecs_set_id, 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_id(world, entity, ecs_id(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_id(world, entity, ecs_id(component), sizeof(component), &(component)__VA_ARGS__) #endif /** @} */ /** * @defgroup singleton Singleton components * @{ */ #define ecs_singleton_get(world, comp)\ ecs_get(world, ecs_id(comp), comp) #ifndef FLECS_LEGACY #define ecs_singleton_set(world, comp, ...)\ ecs_set(world, ecs_id(comp), comp, __VA_ARGS__) #endif #define ecs_singleton_get_mut(world, comp)\ ecs_get_mut(world, ecs_id(comp), comp, NULL) #define ecs_singleton_modified(world, comp)\ ecs_modified(world, ecs_id(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 id The id to test for. * @return True if the entity has the entity, false if not. */ FLECS_API bool ecs_has_id( const ecs_world_t *world, ecs_entity_t entity, ecs_id_t id); /** 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. */ #ifndef ecs_has #define ecs_has(world, entity, type)\ ecs_has_id(world, entity, ecs_id(type)) #endif /** 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 IsA pair. * * @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. */ #ifndef ecs_owns #define ecs_owns(world, entity, has, owned)\ ecs_type_has_id(world, ecs_get_type(world, entity), has, owned) #endif /** @} */ /** * @defgroup metadata Entity Metadata * @{ */ /** Test whether an entity is valid. * Entities that are valid can be used with API functions. * * An entity is valid if it is not 0 and if it is alive. If the provided id has * a role or a pair, the contents of the role or the pair will be checked for * validity. * * is_valid will return true for ids that don't exist (alive or not alive). This * allows for using ids that have never been created by ecs_new or similar. In * this the function differs from ecs_is_alive, which will return false for * entities that do not yet exist. * * The operation will return false for an id that exists and is not alive, as * using this id with an API operation would cause it to assert. * * @param world The world. * @param e The entity. * @return True if the entity is valid, false if the entity is not valid. */ FLECS_API bool ecs_is_valid( const ecs_world_t *world, ecs_entity_t e); /** Test whether an entity is alive. * An entity is alive when it has been returned by ecs_new (or similar) or if * it is not empty (componentts have been explicitly added to the id). * * @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( const ecs_world_t *world, ecs_entity_t e); /** Get alive identifier. * In some cases an application may need to work with identifiers from which * the generation has been stripped. A typical scenario in which this happens is * when iterating relationships in an entity type. * * For example, when obtaining the parent id from a ChildOf relation, the parent * (object part of the pair) will have been stored in a 32 bit value, which * cannot store the entity generation. This function can retrieve the identifier * with the current generation for that id. * * If the provided identifier is not alive, the function will return 0. * * @param world The world. * @param e The for which to obtain the current alive entity id. * @return The alive entity id if there is one, or 0 if the id is not alive. */ FLECS_API ecs_entity_t ecs_get_alive( const ecs_world_t *world, ecs_entity_t e); /** Ensure id is alive. * This operation ensures that the provided id is alive. This is useful in * scenarios where an application has an existing id that has not been created * with ecs_new (such as a global constant or an id from a remote application). * * Before this operation the id must either not yet exist, or must exist with * the same generation as the provided id. If the id has been recycled and the * provided id does not have the same generation count, the function will fail. * * If the provided entity is not alive, and the provided generation count is * equal to the current generation (which is the future generation when the id * will be recycled) the id will become alive again. * * If the provided id has a non-zero generation count and the id does not exist * in the world, the id will be created with the specified generation. * * This behavior ensures that an application can use ecs_ensure to track the * lifecycle of an id without explicitly having to create it. It also protects * against reviving an id with a generation count that was not yet due. * * @param world The world. * @param entity The entity id to make alive. */ FLECS_API void ecs_ensure( 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( const 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( const ecs_world_t *world, ecs_entity_t entity); /** Get the table of an entity. * * @param world The world. * @param entity The entity. * @return The table of the entity, NULL if the entity has no components. */ FLECS_API ecs_table_t* ecs_get_table( const 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( const ecs_world_t *world, ecs_id_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( const ecs_world_t *world, ecs_entity_t entity); /** Get the symbol of an entity. * This will return the name as specified in the EcsSymbol 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_symbol( const ecs_world_t *world, ecs_entity_t entity); /** Set the name of an entity. * This will set or overwrite the name of an entity. If no entity is provided, * a new entity will be created. * * The name will be stored in the EcsName component. * * @param world The world. * @param entity The entity. * @param name The entity's name. * @return The provided entity, or a new entity if 0 was provided. */ FLECS_API ecs_entity_t ecs_set_name( ecs_world_t *world, ecs_entity_t entity, const char *name); /** Set the symbol of an entity. * This will set or overwrite the symbol of an entity. If no entity is provided, * a new entity will be created. * * The symbol will be stored in the EcsName component. * * @param world The world. * @param entity The entity. * @param symbol The entity's symbol. * @return The provided entity, or a new entity if 0 was provided. */ FLECS_API ecs_entity_t ecs_set_symbol( ecs_world_t *world, ecs_entity_t entity, const char *symbol); /** 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 id to string. * This operation interprets the structure of an id and converts it to a string. * * @param world The world. * @param id The id 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_id_str( const ecs_world_t *world, ecs_id_t entity, char *buffer, size_t buffer_len); /** Get the object of a relation. * This will return a object of the entity for the specified relation. The index * allows for iterating through the objects, if a single entity has multiple * objects for the same relation. * * If the index is larger than the total number of instances the entity has for * the relation, the operation will return 0. * * @param world The world. * @param entity The entity. * @param rel The relation between the entity and the object. * @param index The index of the relation instance. * @return The object for the relation at the specified index. */ FLECS_API ecs_entity_t ecs_get_object( const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t rel, int32_t index); /** 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 the specified id. * Returns the number of entities that have the specified id. * * @param world The world. * @param entity The id to search for. * @return The number of entities that have the id. */ FLECS_API int32_t ecs_count_id( const ecs_world_t *world, ecs_id_t entity); /** 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_filter( const 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( const 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( const 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. * @param recursive Recursively traverse up the tree until entity is found. * @return The entity if found, else 0. */ FLECS_API ecs_entity_t ecs_lookup_path_w_sep( const ecs_world_t *world, ecs_entity_t parent, const char *path, const char *sep, const char *prefix, bool recursive); /** 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, true) /** Lookup an entity from a full path. * Same as ecs_lookup_path, 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, true) /** 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( const ecs_world_t *world, const char *symbol, bool lookup_as_path); /* 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. * @return The relative entity path. */ FLECS_API char* ecs_get_path_w_sep( const ecs_world_t *world, ecs_entity_t parent, ecs_entity_t child, 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, ".", 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, ".", 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( const 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( const ecs_world_t *world); /** Set current with id. * New entities are automatically created with the specified id. * * @param world The world. * @param id The id. * @return The previous id. */ FLECS_API ecs_entity_t ecs_set_with( ecs_world_t *world, ecs_id_t id); /** Get current with id. * Get the id set with ecs_set_with. * * @param world The world. * @param id The id. * @return The previous id. */ FLECS_API ecs_entity_t ecs_get_with( const 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 terms Terms * @{ */ /** Iterator for a single (component) id. * A term iterator returns all entities (tables) that match a single (component) * id. The search for the matching set of entities (tables) is performed in * constant time. * * Currently only trivial terms are supported (see ecs_term_is_trivial). Only * the id field of the term needs to be initialized. * * @param world The world. * @param term The term. * @return The iterator. */ FLECS_API ecs_iter_t ecs_term_iter( ecs_world_t *world, ecs_term_t *term); /** Progress the term iterator. * This operation progresses the term iterator to the next table. The * iterator must have been initialized with `ecs_term_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_term_next( ecs_iter_t *it); /** Test whether term id is set. * * @param id The term id. * @return True when set, false when not set. */ FLECS_API bool ecs_term_id_is_set( const ecs_term_id_t *id); /** Test whether a term is set. * This operation can be used to test whether a term has been initialized with * values or whether it is empty. * * An application generally does not need to invoke this operation. It is useful * when initializing a 0-initialized array of terms (like in ecs_term_desc_t) as * this operation can be used to find the last initialized element. * * @param term The term. * @return True when set, false when not set. */ FLECS_API bool ecs_term_is_initialized( const ecs_term_t *term); /** Test whether a term is a trivial term. * A trivial term is a term that only contains a type id. Trivial terms must not * have read/write annotations, relation substitutions and subjects other than * 'This'. Examples of trivial terms are: * - 'Position' * - 'Position(This)' * - '(Likes, IceCream)' * - 'Likes(This, IceCream)' * * Examples of non-trivial terms are: * - '[in] Position' * - 'Position(MyEntity)' * - 'Position(self|superset)' * * Trivial terms are useful in expressions that should just represent a list of * components, such as when parsing the list of components to add to an entity. * * The term passed to this operation must be finalized. Terms returned by the * parser are guaranteed to be finalized. * * @param term The term. * @return True if term is trivial, false if it is not. */ FLECS_API bool ecs_term_is_trivial( const ecs_term_t *term); /** Finalize term. * Ensure that all fields of a term are consistent and filled out. This * operation should be invoked before using and after assigning members to, or * parsing a term. When a term contains unresolved identifiers, this operation * will resolve and assign the identifiers. If the term contains any identifiers * that cannot be resolved, the operation will fail. * * An application generally does not need to invoke this operation as the APIs * that use terms (such as filters, queries and triggers) will finalize terms * when they are created. * * The name and expr parameters are optional, and only used for giving more * descriptive error messages. * * @param world The world. * @param name The name of the entity that uses the term (such as a system). * @param expr The string expression of which the term is a part. * @param term The term to finalize. * @return Zero if success, nonzero if an error occurred. */ FLECS_API int ecs_term_finalize( const ecs_world_t *world, const char *name, const char *expr, ecs_term_t *term); /** Copy resources of a term to another term. * This operation copies one term to another term. If the source term contains * allocated resources (such as identifiers), they will be duplicated so that * no memory is shared between the terms. * * @param dst The term to copy to. * @param src The term to copy from. */ FLECS_API ecs_term_t ecs_term_copy( const ecs_term_t *src); /** Move resources of a term to another term. * Same as copy, but moves resources from src, if src->move is set to true. If * src->move is not set to true, this operation will do a copy. * * The conditional move reduces redundant allocations in scenarios where a list * of terms is partially created with allocated resources. * * @param dst The term to copy to. * @param src The term to copy from. */ FLECS_API ecs_term_t ecs_term_move( ecs_term_t *src); /** Free resources of term. * This operation frees all resources (such as identifiers) of a term. The term * object itself is not freed. * * @param term The term to free. */ FLECS_API void ecs_term_fini( ecs_term_t *term); /** Utility to match an id with a pattern. * This operation returns true if the provided pattern matches the provided * id. The pattern may contain a wildcard (or wildcards, when a pair). * * @param id The id. * @param pattern The pattern to compare with. */ FLECS_API bool ecs_id_match( ecs_id_t id, ecs_id_t pattern); /** Utility to check if id is a pair. * * @param id The id. * @return True if id is a pair. */ FLECS_API bool ecs_id_is_pair( ecs_id_t id); /** Utility to check if id is a wildcard. * * @param id The id. * @return True if id is a wildcard or a pair containing a wildcard. */ FLECS_API bool ecs_id_is_wildcard( ecs_id_t id); /** @} */ /** * @defgroup filters Filters * @{ */ /** Initialize filter * A filter is a lightweight object that can be used to query for entities in * a world. Filters, as opposed to queries, do not cache results. They are * therefore slower to iterate, but are faster to create. * * This operation will at minimum allocate an array to hold the filter terms in * the returned filter struct. It may allocate additional memory if the provided * description contains a name, expression, or if the provided array of terms * contains strings (identifier names or term names). * * It is possible to create a filter without allocating any memory, by setting * the "terms" and "term_count" members directly. When doing so an application * should not call ecs_filter_init but ecs_filter_finalize. This will ensure * that all fields are consistent and properly filled out. * * @param world The world. * @param desc Properties for the filter to create. * @param filter_out The filter. * @return Zero if successful, non-zero if failed. */ FLECS_API int ecs_filter_init( const ecs_world_t *world, ecs_filter_t *filter_out, const ecs_filter_desc_t *desc); /** Deinitialize filter. * Free resources associated with filter. * * @param filter The filter to deinitialize. */ FLECS_API void ecs_filter_fini( ecs_filter_t *filter); /** Finalize filter. * When manually assigning an array of terms to the filter struct (so not when * using ecs_filter_init), this operation should be used to ensure that all * terms are assigned properly and all (derived) fields have been set. * * When ecs_filter_init is used to create the filter, this function should not * be called. The purpose of this operation is to support creation of filters * without allocating memory. * * @param filter The filter to finalize. * @return Zero if filter is valid, non-zero if it contains errors. * @ */ FLECS_API int ecs_filter_finalize( const ecs_world_t *world, ecs_filter_t *filter); /** Convert filter to string expression. * Convert filter terms to a string expression. The resulting expression can be * parsed to create the same filter. */ FLECS_API char* ecs_filter_str( const ecs_world_t *world, const ecs_filter_t *filter); /** 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); /** Move resources of one filter to another. */ FLECS_API void ecs_filter_move( ecs_filter_t *dst, ecs_filter_t *src); /** Copy resources of one filter to another. */ FLECS_API void ecs_filter_copy( ecs_filter_t *dst, const ecs_filter_t *src); /** @} */ /** * @defgroup queries Queries * @{ */ /** Create a query. * This operation creates a query. Queries are used to iterate over entities * that match a filter 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_fini) before the world is deleted, it will be deleted * automatically. * * @param world The world. * @param desc A structure describing the query properties. * @return The new query. */ FLECS_API ecs_query_t* ecs_query_init( ecs_world_t *world, const ecs_query_desc_t *desc); /** Destroy a query. * This operation destroys a query and its resources. If the query is used as * the parent of subqueries, those subqueries will be orphaned and must be * deinitialized as well. * * @param query The query. */ FLECS_API void ecs_query_fini( ecs_query_t *query); /** Get filter object of query. * This operation obtains a pointer to the internally constructed filter object * of the query and can be used to introspect the query terms. * * @param query The query. */ FLECS_API const ecs_filter_t* ecs_query_get_filter( 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 stage_current Id of current stage. * @param stage_count Total number of stages. * @returns True if more data is available, false if not. */ FLECS_API bool ecs_query_next_worker( ecs_iter_t *it, int32_t stage_current, int32_t stage_count); /** 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 trigger Triggers */ /** Create trigger. * Triggers notify the application when certain events happen such as adding or * removing components. * * An application can change the trigger callback or context pointer by calling * ecs_trigger_init for an existing trigger entity, by setting the * ecs_trigger_desc_t::entity.entity field in combination with callback and/or * ctx. * * See the documentation for ecs_trigger_desc_t for more details. * * @param world The world. * @param decs The trigger creation parameters. */ FLECS_API ecs_entity_t ecs_trigger_init( ecs_world_t *world, const ecs_trigger_desc_t *desc); /** Get trigger context. * This operation returns the context pointer set for the trigger. If * the provided entity is not a trigger, the function will return NULL. * * @param world The world. * @param trigger The trigger from which to obtain the context. * @return The context. */ FLECS_API void* ecs_get_trigger_ctx( const ecs_world_t *world, ecs_entity_t trigger); /** Same as ecs_get_trigger_ctx, but for binding ctx. * The binding context is a context typically used to attach any language * binding specific data that is needed when invoking a callback that is * implemented in another language. * * @param world The world. * @param trigger The trigger from which to obtain the context. * @return The context. */ FLECS_API void* ecs_get_trigger_binding_ctx( const ecs_world_t *world, ecs_entity_t trigger); typedef enum ecs_payload_kind_t { EcsPayloadNone, EcsPayloadEntity, EcsPayloadTable } ecs_payload_kind_t; typedef struct ecs_event_desc_t { ecs_entity_t event; ecs_ids_t *ids; /* When NULL, notify for all ids in entity/table type */ ecs_payload_kind_t payload_kind; union { ecs_entity_t entity; struct { ecs_table_t *table; int32_t offset; int32_t count; /* When 0 notify all entities starting from offset */ } table; } payload; void *param; /* Assigned to iter param member */ /* Observable for which to notify the triggers/observers. If NULL, the * world will be used as observable. */ ecs_object_t *observable; } ecs_event_desc_t; /** Send event. */ FLECS_API void ecs_emit( ecs_world_t *world, ecs_event_desc_t *desc); /** @} */ /** * @defgroup observer Observers */ /** Create observer. * Observers are like triggers, but can subscribe for multiple terms. An * observer only triggers when the source of the event meets all terms. * * See the documentation for ecs_observer_desc_t for more details. * * @param world The world. * @param desc The observer creation parameters. */ FLECS_API ecs_entity_t ecs_observer_init( ecs_world_t *world, const ecs_observer_desc_t *desc); FLECS_API void* ecs_get_observer_ctx( const ecs_world_t *world, ecs_entity_t observer); FLECS_API void* ecs_get_observer_binding_ctx( const ecs_world_t *world, ecs_entity_t observer); /** @} */ /** * @defgroup iterator Iterators * @{ */ /** Obtain data for a query term. * This operation retrieves a pointer to an array of data that belongs to the * term in the query. The index refers to the location of the term in the query, * and starts counting from one. * * For example, the query "Position, Velocity" will return the Position array * for index 1, and the Velocity array for index 2. * * When the specified term is not owned by the entity this function returns a * pointer instead of an array. This happens when the source of a term is not * the entity being iterated, such as a shared component (from a prefab), a * component from a parent, or another entity. The ecs_term_is_owned operation * can be used to test dynamically if a term is owned. * * The provided size must be either 0 or must match the size of the datatype * of the returned array. If the size does not match, the operation may assert. * The size can be dynamically obtained with ecs_term_size. * * @param it The iterator. * @param size The size of the returned array. * @param index The index of the term in the query. * @return A pointer to the data associated with the term. */ FLECS_API void* ecs_term_w_size( const ecs_iter_t *it, size_t size, int32_t index); /** Same as ecs_term_w_size, but accepts a type instead of a size. */ #define ecs_term(it, T, index)\ ((T*)ecs_term_w_size(it, sizeof(T), index)) /** Obtain the component/pair id for a term. * This operation retrieves the id for the specified query term. Typically this * is the component id, but it can also be a pair id or a role annotated id, * depending on the term. * * @param it The iterator. * @param index The index of the term in the query. * @return The id associated with te term. */ FLECS_API ecs_id_t ecs_term_id( const ecs_iter_t *it, int32_t index); /** Obtain the source for a term. * This operation retrieves the source of the specified term. A source is the * entity from which the data is retrieved. If the term is owned by the iterated * over entity/entities, the function will return id 0. * * This operation can be useful to retrieve, for example, the id of a parent * entity when a component from a parent has been requested, or to retrieve the * id from a prefab, in the case of a shared component. * * @param it The iterator. * @param index The index of the term in the query. * @return The source associated with te term. */ FLECS_API ecs_entity_t ecs_term_source( const ecs_iter_t *it, int32_t index); /** Obtain the size for a term. * This operation retrieves the size of the datatype for the term. * * @param it The iterator. * @param index The index of the term in the query. * @return The size of the datatype associated with te term. */ FLECS_API size_t ecs_term_size( const ecs_iter_t *it, int32_t index); /** Test whether the term is readonly * This operation returns whether this is a readonly term. Readonly terms are * annotated with [in], or are added as a const type in the C++ API. * * @param it The iterator. * @param index The index of the term in the query. * @return Whether the term is readonly. */ FLECS_API bool ecs_term_is_readonly( const ecs_iter_t *it, int32_t index); /** Test whether term is set. * This function returns false for terms with the Not operator and for terms * with the Optional operator if the matched entities (table) do not have the * (component) id of the term. * * @param it The iterator. * @param term The term. * @return True if term is set, false if it is not set. */ FLECS_API bool ecs_term_is_set( const ecs_iter_t *it, int32_t term); /** Test whether the term is owned * This operation returns whether the term is owned by the currently iterated * entity. This function will return false when the term is owned by another * entity, such as a parent or a prefab. * * @param it The iterator. * @param index The index of the term in the query. * @return Whether the term is owned by the iterated over entity/entities. */ FLECS_API bool ecs_term_is_owned( const ecs_iter_t *it, int32_t index); /** Get the type of the currently entities. * This operation returns the type of the current iterated entity/entities. A * type is a vector that contains all ids of the components that an entity has. * * @param it The iterator. * @return The type of the currently iterated entity/entities. */ FLECS_API ecs_type_t ecs_iter_type( const ecs_iter_t *it); /** Get the table for the current entities. * This operation returns the table of the current iterated entities * * @param it The iterator. * @return The table of the currently iterated entity/entities. */ FLECS_API ecs_table_t* ecs_iter_table( const ecs_iter_t *it); /** Find the column index for a given id. * This operation finds the index of a column in the current type for the * specified id. For example, if an entity has type Position, Velocity, and the * application requests the id for the Velocity component, this function will * return 1. * * Note that the column index returned by this function starts from 0, as * opposed to 1 for the terms. The reason for this is that the returned index * is equivalent to using the ecs_type_get_index function, with as type the * value returned by ecs_iter_type. * * This operation can be used to request columns that are not requested by a * query. For example, a query may request Position, Velocity, but an entity * may also have Mass. With this function the iterator can request the data for * Mass as well, when used in combination with ecs_iter_column. * * @param it The iterator. * @return The type of the currently iterated entity/entities. */ FLECS_API int32_t ecs_iter_find_column( const ecs_iter_t *it, ecs_id_t id); /** Obtain data for a column index. * This operation can be used with the id obtained from ecs_iter_find_column to * request data from the currently iterated over entity/entities that is not * requested by the query. * * The data in the returned pointer can be accessed using the same index as * the one used to access the arrays returned by the ecs_term function. * * The provided size must be either 0 or must match the size of the datatype * of the returned array. If the size does not match, the operation may assert. * The size can be dynamically obtained with ecs_iter_column_size. * * Note that this function can be used together with ecs_iter_type to * dynamically iterate all data that the matched entities have. An application * can use the ecs_vector_count function to obtain the number of elements in a * type. All indices from 0..ecs_vector_count(type) are valid column indices. * * Additionally, note that this provides unprotected access to the column data. * An iterator cannot know or prevent accessing columns that are not queried for * and thus applications should only use this when it can be guaranteed that * there are no other threads reading/writing the same column data. * * @param it The iterator. * @param size The size of the column. * @param index The index of the column. * @return The data belonging to the column. */ FLECS_API void* ecs_iter_column_w_size( const ecs_iter_t *it, size_t size, int32_t index); /** Same as ecs_iter_column_w_size, but accepts a type instead of a size. */ #define ecs_iter_column(it, T, index)\ ((T*)ecs_iter_column_w_size(it, sizeof(T), index)) /** Obtain size for a column index. * This operation obtains the size for a column. The size is equal to the size * of the datatype associated with the column. * * @param it The iterator. * @param index The index of the column. * @return The size belonging to the column. */ FLECS_API size_t ecs_iter_column_size( const ecs_iter_t *it, int32_t index); /** @} */ /** * @defgroup staging Staging * @{ */ /** Begin frame. * When an application does not use ecs_progress to control the main loop, it * can still use Flecs features such as FPS limiting and time measurements. This * operation needs to be invoked whenever a new frame is about to get processed. * * Calls to ecs_frame_begin must always be followed by ecs_frame_end. * * The function accepts a delta_time parameter, which will get passed to * systems. This value is also used to compute the amount of time the function * needs to sleep to ensure it does not exceed the target_fps, when it is set. * When 0 is provided for delta_time, the time will be measured. * * This function should only be ran from the main thread. * * @param world The world. * @param delta_time Time elapsed since the last frame. * @return The provided delta_time, or measured time if 0 was provided. */ FLECS_API FLECS_FLOAT ecs_frame_begin( ecs_world_t *world, FLECS_FLOAT delta_time); /** End frame. * This operation must be called at the end of the frame, and always after * ecs_frame_begin. * * @param world The world. */ FLECS_API void ecs_frame_end( ecs_world_t *world); /** Begin staging. * When an application does not use ecs_progress to control the main loop, it * can still use Flecs features such as the defer queue. When an application * needs to stage changes, it needs to call this function after ecs_frame_begin. * A call to ecs_staging_begin must be followed by a call to ecs_staging_end. * * 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. * * While the world is in staging mode, no structural changes (add/remove/...) * can be made to the world itself. Operations must be executed on a stage * instead (see ecs_get_stage). * * This function should only be ran from the main thread. * * @param world The world * @return Whether world is currently staged. */ FLECS_API bool ecs_staging_begin( ecs_world_t *world); /** End staging. * Leaves staging mode. After this operation the world may be directly mutated * again. By default this operation also merges data back into the world, unless * automerging was disabled explicitly. * * This function should only be ran from the main thread. * * @param world The world */ FLECS_API void ecs_staging_end( ecs_world_t *world); /** Merge world or stage. * When automatic merging is disabled, an application can call this * operation on either an individual stage, or on the world which will merge * all stages. This operation may only be called when staging is not enabled * (either after progress() or after staging_end()). * * This operation may be called on an already merged stage or world. * * @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 world changed from non-deferred mode to deferred mode. */ FLECS_API bool ecs_defer_begin( ecs_world_t *world); /** Test if deferring is enabled for current stage. * * @param world The world. * @return True if deferred, false if not. */ FLECS_API bool ecs_is_deferred( const 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 for world or stage. * When automerging is enabled, staged data will automatically be merged with * the world when staging ends. This happens at the end of progress(), at a * sync point or when staging_end() is called. * * Applications can exercise more control over when data from a stage is merged * by disabling automerging. This requires an application to explicitly call * merge() on the stage. * * When this function is invoked on the world, it sets all current stages to * the provided value and sets the default for new stages. When this function is * invoked on a stage, automerging is only set for that specific stage. * * @param world The world. * @param automerge Whether to enable or disable automerging. */ FLECS_API void ecs_set_automerge( ecs_world_t *world, bool automerge); /** Configure world to have N stages. * This initializes N stages, which allows applications to defer operations to * multiple isolated defer queues. This is typically used for applications with * multiple threads, where each thread gets its own queue, and commands are * merged when threads are synchronized. * * Note that the ecs_set_threads function already creates the appropriate * number of stages. The set_stages() operation is useful for applications that * want to manage their own stages and/or threads. * * @param world The world. * @param stages The number of stages. */ FLECS_API void ecs_set_stages( ecs_world_t *world, int32_t stages); /** Get number of configured stages. * Return number of stages set by ecs_set_stages. * * @param world The world. * @return The number of stages used for threading. */ FLECS_API int32_t ecs_get_stage_count( const ecs_world_t *world); /** Get current stage id. * The stage id can be used by an application to learn about which stage it is * using, which typically corresponds with the worker thread id. * * @param world The world. * @return The stage id. */ FLECS_API int32_t ecs_get_stage_id( const ecs_world_t *world); /** Get stage-specific world pointer. * Flecs threads can safely invoke the API as long as they have a private * context to write to, also referred to as the stage. This function returns a * pointer to a stage, disguised as a world pointer. * * Note that this function does not(!) create a new world. It simply wraps the * existing world in a thread-specific context, which the API knows how to * unwrap. The reason the stage is returned as an ecs_world_t is so that it * can be passed transparently to the existing API functions, vs. having to * create a dediated API for threading. * * @param world The world. * @param stage_id The index of the stage to retrieve. * @return A thread-specific pointer to the world. */ FLECS_API ecs_world_t* ecs_get_stage( const ecs_world_t *world, int32_t stage_id); /** Get actual world from world. * @param world A pointer to a stage or the world. * @return The world. */ FLECS_API const ecs_world_t* ecs_get_world( const ecs_world_t *world); /** Test whether the current world object is readonly. * This function allows the code to test whether the currently used world object * is readonly or whether it allows for writing. * * @param world A pointer to a stage or the world. * @return True if the world or stage is readonly. */ FLECS_API bool ecs_stage_is_readonly( const ecs_world_t *stage); /** Create asynchronous stage. * An asynchronous stage can be used to asynchronously queue operations for * later merging with the world. An asynchronous stage is similar to a regular * stage, except that it does not allow reading from the world. * * Asynchronous stages are never merged automatically, and must therefore be * manually merged with the ecs_merge function. It is not necessary to call * defer_begin or defer_end before and after enqueuing commands, as an * asynchronous stage unconditionally defers operations. * * The application must ensure that no commands are added to the stage while the * stage is being merged. * * An asynchronous stage must be cleaned up by ecs_async_stage_free. * * @param world The world. * @return The stage. */ FLECS_API ecs_world_t* ecs_async_stage_new( ecs_world_t *world); /** Free asynchronous stage. * The provided stage must be an asynchronous stage. If a non-asynchronous stage * is provided, the operation will fail. * * @param stage The stage to free. */ FLECS_API void ecs_async_stage_free( ecs_world_t *stage); /** Test whether provided stage is asynchronous. * * @param stage The stage. * @return True when the stage is asynchronous, false for a regular stage or * world. */ FLECS_API bool ecs_stage_is_async( ecs_world_t *stage); /** @} */ /** * @defgroup table_functions Public table operations * @brief Low-level table functions. These functions are intended to enable the * creation of higher-level operations. It is not recommended to use * these operations directly in application code as they do not provide * the same safety guarantees as the other APIs. * @{ */ /** 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( const 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( const ecs_table_t *table); /** Get table that has all components of current table plus the specified id. * If the provided table already has the provided id, the operation will return * the provided table. * * @param world The world. * @param table The table. * @param id The id to add. * @result The resulting table. */ FLECS_API ecs_table_t* ecs_table_add_id( ecs_world_t *world, ecs_table_t *table, ecs_id_t id); /** Get table that has all components of current table minus the specified id. * If the provided table doesn't have the provided id, the operation will return * the provided table. * * @param world The world. * @param table The table. * @param id The id to remove. * @result The resulting table. */ FLECS_API ecs_table_t* ecs_table_remove_id( ecs_world_t *world, ecs_table_t *table, ecs_id_t id); /** Lock or unlock table. * When a table is locked, modifications to it will trigger an assert. When the * table is locked recursively, it will take an equal amount of unlock * operations to actually unlock the table. * * Table locks can be used to build safe iterators where it is guaranteed that * the contents of a table are not modified while it is being iterated. * * The operation only works when called on the world, and has no side effects * when called on a stage. The assumption is that when called on a stage, * operations are deferred already. * * @param world The world. * @param table The table to lock. */ FLECS_API void ecs_table_lock( ecs_world_t *world, ecs_table_t *table); /** Unlock a table. * Must be called after calling ecs_table_lock. * * @param world The world. * @param table The table to unlock. */ FLECS_API void ecs_table_unlock( ecs_world_t *world, ecs_table_t *table); /** Returns whether table is a module or contains module contents * Returns true for tables that have module contents. Can be used to filter out * tables that do not contain application data. * * @param table The table. * @return true if table contains module contents, false if not. */ FLECS_API bool ecs_table_has_module( ecs_table_t *table); /** Commit (move) entity to a table. * This operation moves an entity from its current table to the specified * table. This may trigger the following actions: * - Ctor for each component in the target table * - Move for each overlapping component * - Dtor for each component in the source table. * - OnAdd triggers for non-overlapping components in the target table * - OnRemove triggers for non-overlapping components in the source table. * * This operation is a faster than adding/removing components individually. * * The application must explicitly provide the difference in components between * tables as the added/removed parameters. This can usually be derived directly * from the result of ecs_table_add_id and esc_table_remove_id. These arrays are * required to properly execute OnAdd/OnRemove triggers. * * @param world The world. * @param entity The entity to commit. * @param record The entity's record (optional, providing it saves a lookup). * @param table The table to commit the entity to. * @return True if the entity got moved, false otherwise. */ FLECS_API bool ecs_commit( ecs_world_t *world, ecs_entity_t entity, ecs_record_t *record, ecs_table_t *table, ecs_ids_t *added, ecs_ids_t *removed); /** @} */ /* Optional modules */ #ifdef FLECS_SYSTEM /** * @file system.h * @brief System module. * * The system module allows for creating and running systems. A system is a * query in combination with a callback function. In addition systems have * support for time management and can be monitored by the stats addon. */ #ifdef FLECS_SYSTEM #ifndef FLECS_MODULE #define FLECS_MODULE #endif /** * @file module.h * @brief Module addon. * * The module addon allows for creating and importing modules. Flecs modules * enable applications to organize components and systems into reusable units of * code that can easily be across projects. */ #ifdef FLECS_MODULE #ifndef FLECS_MODULE_H #define FLECS_MODULE_H #ifdef __cplusplus extern "C" { #endif /** 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. */ FLECS_API ecs_entity_t ecs_import_from_library( ecs_world_t *world, const char *library_name, const char *module_name); /** Register a new module. */ FLECS_API ecs_entity_t ecs_module_init( ecs_world_t *world, const ecs_component_desc_t *desc); /** Define module */ #define ECS_MODULE(world, id)\ ecs_entity_t ecs_id(id) = ecs_module_init(world, &(ecs_component_desc_t){\ .entity = {\ .name = #id,\ .add = {EcsModule}\ },\ .size = sizeof(id),\ .alignment = ECS_ALIGNOF(id)\ });\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &FLECS__E##id, 1);\ id *handles = (id*)ecs_get_mut(world, ecs_id(id), id, NULL);\ ecs_set_scope(world, ecs_id(id));\ (void)ecs_id(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. The module component will be created as a singleton. * * 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_id_t ecs_id(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_id(id);\ (void)ecs_type(id);\ /** Utility macro for declaring a component inside a handles type */ #define ECS_DECLARE_COMPONENT(id)\ ecs_id_t ecs_id(id);\ ecs_type_t ecs_type(id) /** Utility macro for declaring an entity inside a handles type */ #define ECS_DECLARE_ENTITY(id)\ ecs_entity_t id;\ ecs_type_t ecs_type(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_id(id) = ecs_id(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_id_t ecs_id(id) = (handles).ecs_id(id); (void)ecs_id(id);\ ECS_VECTOR_STACK(FLECS__T##id, ecs_entity_t, &FLECS__E##id, 1);\ (void)ecs_id(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(EcsSystem), ecs_type(EcsTickSource); /* 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; //////////////////////////////////////////////////////////////////////////////// //// Systems API //////////////////////////////////////////////////////////////////////////////// /** System status change callback */ typedef enum ecs_system_status_t { EcsSystemStatusNone = 0, EcsSystemEnabled, EcsSystemDisabled, EcsSystemActivated, EcsSystemDeactivated } ecs_system_status_t; /** 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. */ typedef void (*ecs_system_status_action_t)( ecs_world_t *world, ecs_entity_t system, ecs_system_status_t status, void *ctx); /* Use with ecs_system_init */ typedef struct ecs_system_desc_t { /* System entity creation parameters */ ecs_entity_desc_t entity; /* System query parameters */ ecs_query_desc_t query; /* System callback, invoked when system is ran */ ecs_iter_action_t callback; /* System status callback, invoked when system status changes */ ecs_system_status_action_t status_callback; /* Associate with entity */ ecs_entity_t self; /* Context to be passed to callback (as ecs_iter_t::param) */ void *ctx; /* Context to be passed to system status callback */ void *status_ctx; /* Binding context, for when system is implemented in other language */ void *binding_ctx; /* Functions that are invoked during system cleanup to free context data. * When set, functions are called unconditionally, even when the ctx * pointers are NULL. */ ecs_ctx_free_t ctx_free; ecs_ctx_free_t status_ctx_free; ecs_ctx_free_t binding_ctx_free; /* Interval in seconds at which the system should run */ FLECS_FLOAT interval; /* Rate at which the system should run */ int32_t rate; /* External tick soutce that determines when system ticks */ ecs_entity_t tick_source; } ecs_system_desc_t; /* Create a system */ FLECS_API ecs_entity_t ecs_system_init( ecs_world_t *world, const ecs_system_desc_t *desc); #ifndef FLECS_LEGACY #define ECS_SYSTEM(world, id, kind, ...) \ ecs_iter_action_t ecs_iter_action(id) = id;\ ecs_entity_t id = ecs_system_init(world, &(ecs_system_desc_t){\ .entity = { .name = #id, .add = {kind} },\ .query.filter.expr = #__VA_ARGS__,\ .callback = ecs_iter_action(id)\ });\ ecs_assert(id != 0, ECS_INVALID_PARAMETER, NULL);\ (void)ecs_iter_action(id);\ (void)id; #endif /* Deprecated, use ecs_trigger_init */ #define ECS_TRIGGER(world, trigger_name, kind, component) \ ecs_entity_t __F##trigger_name = ecs_trigger_init(world, &(ecs_trigger_desc_t){\ .entity.name = #trigger_name,\ .callback = trigger_name,\ .expr = #component,\ .events = {kind},\ });\ ecs_entity_t trigger_name = __F##trigger_name;\ ecs_assert(trigger_name != 0, ECS_INVALID_PARAMETER, NULL);\ (void)__F##trigger_name;\ (void)trigger_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); /** Same as ecs_run, but subdivides entities across number of provided stages. * * @param world The world. * @param system The system to run. * @param stage_current The id of the current stage. * @param stage_count The total number of stages. * @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_worker( ecs_world_t *world, ecs_entity_t system, int32_t stage_current, int32_t stage_count, 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); /** Get the query object for a system. * Systems use queries under the hood. This enables an application to get access * to the underlying query object of a system. This can be useful when, for * example, an application needs to enable sorting for a system. * * @param world The world. * @param system The system from which to obtain the query. * @return The query. */ FLECS_API ecs_query_t* ecs_get_system_query( const ecs_world_t *world, ecs_entity_t system); /** Get system context. * This operation returns the context pointer set for the system. If * the provided entity is not a system, the function will return NULL. * * @param world The world. * @param system The system from which to obtain the context. * @return The context. */ FLECS_API void* ecs_get_system_ctx( const ecs_world_t *world, ecs_entity_t system); /** Get system binding context. * The binding context is a context typically used to attach any language * binding specific data that is needed when invoking a callback that is * implemented in another language. * * @param world The world. * @param system The system from which to obtain the context. * @return The context. */ FLECS_API void* ecs_get_system_binding_ctx( const ecs_world_t *world, ecs_entity_t system); //////////////////////////////////////////////////////////////////////////////// //// 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 /** * @file pipeline.h * @brief Pipeline module. * * The pipeline module provides support for running systems automatically and * on multiple threads. A pipeline is a collection of tags that can be added to * systems. When ran, a pipeline will query for all systems that have the tags * that belong to a pipeline, and run them. * * The module defines a number of builtin tags (EcsPreUpdate, EcsOnUpdate, * EcsPostUpdate etc.) that are registered with the builtin pipeline. The * builtin pipeline is ran by default when calling ecs_progress(). An * application can set a custom pipeline with the ecs_set_pipeline function. */ #ifdef FLECS_PIPELINE #ifndef FLECS_SYSTEM #define FLECS_SYSTEM #endif #ifndef FLECS_PIPELINE_H #define FLECS_PIPELINE_H #ifdef __cplusplus extern "C" { #endif #ifndef FLECS_LEGACY #define ECS_PIPELINE(world, id, ...) \ ecs_entity_t id = ecs_type_init(world, &(ecs_type_desc_t){\ .entity = {\ .name = #id,\ .add = {EcsPipeline}\ },\ .ids_expr = #__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( const 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 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); /** Run pipeline. * This will run all systems in the provided pipeline. This operation may be * invoked from multiple threads, and only when staging is disabled, as the * pipeline manages staging and, if necessary, synchronization between threads. * * If 0 is provided for the pipeline id, the default pipeline will be ran (this * is either the builtin pipeline or the pipeline set with set_pipeline()). * * When using progress() this operation will be invoked automatically for the * default pipeline (either the builtin pipeline or the pipeline set with * set_pipeline()). An application may run additional pipelines. * * Note: calling this function from an application currently only works in * single threaded applications with a single stage. * * @param world The world. * @param pipeline The pipeline to run. */ FLECS_API void ecs_pipeline_run( ecs_world_t *world, ecs_entity_t pipeline, FLECS_FLOAT delta_time); /** 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); //////////////////////////////////////////////////////////////////////////////// //// 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 /** * @file timer.h * @brief Timer module. * * Timers can be used to trigger actions at periodic or one-shot intervals. They * are typically used together with systems and pipelines. */ #ifdef FLECS_TIMER #ifndef FLECS_MODULE #define FLECS_MODULE #endif #ifndef FLECS_PIPELINE #define FLECS_PIPELINE #endif #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 one shot/interval 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; /* Source of the rate filter */ int32_t rate; /* Rate of the rate filter */ int32_t tick_count; /* Number of times the rate filter ticked */ 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. The timer can be started and stopped with * ecs_start_timer and ecs_stop_timer. * * The timer is synchronous, and is incremented each frame by delta_time. * * The tick_source entity will be be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source 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 tick_source, 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 EcsTimer 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. * * The timer is synchronous, and is incremented each frame by delta_time. * * The tick_source entity will be be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source The timer. * @return The current timeout value, or 0 if no timer is active. */ FLECS_API FLECS_FLOAT ecs_get_timeout( const ecs_world_t *world, ecs_entity_t tick_source); /** 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. * * The timer is synchronous, and is incremented each frame by delta_time. * * The tick_source entity will be be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source 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 tick_source, FLECS_FLOAT interval); /** Get current interval value for the specified timer. * This operation returns the value set by ecs_set_interval. If the entity is * not a timer, the operation will return 0. * * @param world The world. * @param tick_source The timer for which to set the interval. * @return The current interval value, or 0 if no timer is active. */ FLECS_API FLECS_FLOAT ecs_get_interval( const ecs_world_t *world, ecs_entity_t tick_source); /** 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 tick_source The timer to start. */ FLECS_API void ecs_start_timer( ecs_world_t *world, ecs_entity_t tick_source); /** 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 tick_source The timer to stop. */ FLECS_API void ecs_stop_timer( ecs_world_t *world, ecs_entity_t tick_source); /** Set rate filter. * This operation initializes a rate filter. Rate filters sample tick sources * and tick at a configurable multiple. A rate filter is a tick source itself, * which means that rate filters can be chained. * * Rate filters enable deterministic system execution which cannot be achieved * with interval timers alone. For example, if timer A has interval 2.0 and * timer B has interval 4.0, it is not guaranteed that B will tick at exactly * twice the multiple of A. This is partly due to the indeterministic nature of * timers, and partly due to floating point rounding errors. * * Rate filters can be combined with timers (or other rate filters) to ensure * that a system ticks at an exact multiple of a tick source (which can be * another system). If a rate filter is created with a rate of 1 it will tick * at the exact same time as its source. * * If no tick source is provided, the rate filter will use the frame tick as * source, which corresponds with the number of times ecs_progress is called. * * The tick_source entity will be be a tick source after this operation. Tick * sources can be read by getting the EcsTickSource component. If the tick * source ticked this frame, the 'tick' member will be true. When the tick * source is a system, the system will tick when the timer ticks. * * @param world The world. * @param tick_source The rate 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( ecs_world_t *world, ecs_entity_t tick_source, int32_t rate, ecs_entity_t source); /** Assign tick source to system. * Systems can be their own tick source, which can be any of the tick sources * (one shot timers, interval times and rate filters). However, in some cases it * is must be guaranteed that different systems tick on the exact same frame. * * This cannot be guaranteed by giving two systems the same interval/rate filter * as it is possible that one system is (for example) disabled, which would * cause the systems to go out of sync. To provide these guarantees, systems * must use the same tick source, which is what this operation enables. * * When two systems share the same tick source, it is guaranteed that they tick * in the same frame. The provided tick source can be any entity that is a tick * source, including another system. If the provided entity is not a tick source * the system will not be ran. * * To disassociate a tick source from a system, use 0 for the tick_source * parameter. * * @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 /** * @file bulk.h * @brief Bulk operations operate on all entities that match a provided filter. */ #ifdef FLECS_BULK #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_id, 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_id, 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_MODULE #endif #ifdef FLECS_PLECS /** * @file pecs.h * @brief Plecs addon. * * Plecs is a small data definition language for instantiating entities that * reuses the existing flecs query parser. The following examples illustrate * how a plecs snippet translates to regular flecs operations: * * Plecs: * Entity * C code: * ecs_entity_t Entity = ecs_set_name(world, 0, "Entity"); * * Plecs: * Position(Entity) * C code: * ecs_entity_t Position = ecs_set_name(world, 0, "Position"); * ecs_entity_t Entity = ecs_set_name(world, 0, "Entity"); * ecs_add_id(world, Entity, Position); * * Plecs: * Likes(Entity, Apples) * C code: * ecs_entity_t Likes = ecs_set_name(world, 0, "Likes"); * ecs_entity_t Apples = ecs_set_name(world, 0, "Apples"); * ecs_entity_t Entity = ecs_set_name(world, 0, "Entity"); * ecs_add_pair(world, Entity, Likes, Apples); * * A plecs string may contain multiple statements, separated by a newline: * Likes(Entity, Apples) * Likes(Entity, Pears) * Likes(Entity, Bananas) */ #ifdef FLECS_PLECS #define FLECS_PARSER #ifndef FLECS_PLECS_H #define FLECS_PLECS_H #ifdef __cplusplus extern "C" { #endif /** Parse plecs string. * This parses a plecs string and instantiates the entities in the world. * * @param world The world. * @param name The script name (typically the file). * @param str The plecs string. * @return Zero if success, non-zero otherwise. */ FLECS_API int ecs_plecs_from_str( ecs_world_t *world, const char *name, const char *str); /** Parse plecs file. * This parses a plecs file and instantiates the entities in the world. This * operation is equivalent to loading the file contents and passing it to * ecs_plecs_from_str. * * @param world The world. * @param file The plecs file name. * @return Zero if success, non-zero otherwise. */ FLECS_API int ecs_plecs_from_file( ecs_world_t *world, const char *filename); #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_PARSER /** * @file parser.h * @brief Parser addon. * * The parser addon parses string expressions into lists of terms, and can be * used to construct filters, queries and types. */ #ifdef FLECS_PARSER #ifndef FLECS_PARSER_H #define FLECS_PARSER_H #ifdef __cplusplus extern "C" { #endif /** Parse term in expression. * This operation parses a single term in an expression and returns a pointer * to the next term expression. * * If the returned pointer points to the 0-terminator, the expression is fully * parsed. The function would typically be called in a while loop: * * const char *ptr = expr; * while (ptr[0] && (ptr = ecs_parse_term(world, name, expr, ptr, &term))) { } * * The operation does not attempt to find entity ids from the names in the * expression. Use the ecs_term_resolve_ids function to resolve the identifiers * in the parsed term. * * The returned term will in most cases contain allocated resources, which * should freed (or used) by the application. To free the resources for a term, * use the ecs_term_free function. * * The parser accepts expressions in the legacy string format. * * @param world The world. * @param name The name of the expression (optional, improves error logs) * @param expr The expression to parse (optional, improves error logs) * @param ptr The pointer to the current term (must be in expr). * @param term_out Out parameter for the term. * @return pointer to next term if successful, NULL if failed. */ FLECS_API char* ecs_parse_term( const ecs_world_t *world, const char *name, const char *expr, const char *ptr, ecs_term_t *term_out); #ifdef __cplusplus } #endif // __cplusplus #endif // FLECS_PARSER_H #endif // FLECS_PARSER #endif #ifdef FLECS_QUEUE /** * @file queue.h * @brief Queue datastructure. * * The queue data structure implements a fixed-size ringbuffer. It is not used * by the flecs core, but is used by flecs-hub modules. */ #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_SNAPSHOT /** * @file snapshot.h * @brief Snapshot addon. * * A snapshot records the state of a world in a way so that it can be restored * later. Snapshots work with POD components and non-POD components, provided * that the appropriate lifecycle actions are registered for non-POD components. * * A snapshot is tightly coupled to a world. It is not possible to restore a * snapshot from world A into world B. */ #ifdef FLECS_SNAPSHOT #ifndef FLECS_SNAPSHOT_H #define FLECS_SNAPSHOT_H #ifdef __cplusplus extern "C" { #endif /** A snapshot stores the state of a world in a particular point in time. */ typedef struct ecs_snapshot_t ecs_snapshot_t; /** 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 #ifdef FLECS_DIRECT_ACCESS /** * @file direct_access.h * @brief Low-level access to underlying data structures for best performance. * * 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. */ #ifdef FLECS_DIRECT_ACCESS #ifndef FLECS_DIRECT_ACCESS_H_ #define FLECS_DIRECT_ACCESS_H_ #ifdef __cplusplus extern "C" { #endif /** 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( const 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( const 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( const 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( const 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); #ifdef __cplusplus } #endif #endif #endif #endif #ifdef FLECS_STATS /** * @file stats.h * @brief Statistics addon. * * The statistics addon enables an application to obtain detailed metrics about * the storage, systems and operations of a world. */ #ifdef FLECS_STATS #ifndef FLECS_STATS_H #define FLECS_STATS_H #ifdef FLECS_SYSTEM #endif #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( const 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( const 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( const ecs_world_t *world, const ecs_query_t *query, ecs_query_stats_t *s); #ifdef FLECS_SYSTEM /** 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( const ecs_world_t *world, ecs_entity_t system, ecs_system_stats_t *stats); #endif #ifdef FLECS_PIPELINE /** 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( const ecs_world_t *world, ecs_entity_t pipeline, ecs_pipeline_stats_t *stats); #endif 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 /** * @file flecs.hpp * @brief Flecs C++ API. * * This is a C++11 wrapper around the Flecs C API. */ #pragma once // The C++ API does not use STL, save for type_traits #include // Allows overriding flecs_static_assert, which is useful when testing #ifndef flecs_static_assert #define flecs_static_assert(cond, str) static_assert(cond, str) #endif namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// Forward declarations and types //////////////////////////////////////////////////////////////////////////////// using world_t = ecs_world_t; using id_t = ecs_id_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; using ref_t = ecs_ref_t; using iter_t = ecs_iter_t; using ComponentLifecycle = EcsComponentLifecycle; enum inout_kind_t { InOutDefault = EcsInOutDefault, InOut = EcsInOut, In = EcsIn, Out = EcsOut }; enum oper_kind_t { And = EcsAnd, Or = EcsOr, Not = EcsNot, Optional = EcsOptional, AndFrom = EcsAndFrom, OrFrom = EcsOrFrom, NotFrom = EcsNotFrom }; enum var_kind_t { VarDefault = EcsVarDefault, VarIsEntity = EcsVarIsEntity, VarIsVariable = EcsVarIsVariable }; class world; class world_async_stage; class snapshot; class id; class entity; class entity_view; class type; class pipeline; class iter; class term; class filter_iterator; class child_iterator; class world_filter; class snapshot_filter; class query_base; template class filter; template class query; template class system; template class observer; template class filter_builder; template class query_builder; template class system_builder; template class observer_builder; namespace _ { template class cpp_type; template class each_invoker; } //////////////////////////////////////////////////////////////////////////////// //// Builtin components and tags //////////////////////////////////////////////////////////////////////////////// /* Builtin components */ using Component = EcsComponent; using Type = EcsType; using Identifier = EcsIdentifier; using Timer = EcsTimer; using RateFilter = EcsRateFilter; using TickSource = EcsTickSource; using Query = EcsQuery; using Trigger = EcsTrigger; using Observer = EcsObserver; /* Builtin opaque components */ static const flecs::entity_t System = ecs_id(EcsSystem); /* Builtin set constants */ static const uint8_t DefaultSet = EcsDefaultSet; static const uint8_t Self = EcsSelf; static const uint8_t SuperSet = EcsSuperSet; static const uint8_t SubSet = EcsSubSet; static const uint8_t Cascade = EcsCascade; static const uint8_t All = EcsAll; static const uint8_t Nothing = EcsNothing; /* Builtin tag ids */ static const flecs::entity_t Module = EcsModule; static const flecs::entity_t Prefab = EcsPrefab; static const flecs::entity_t Hidden = EcsHidden; static const flecs::entity_t Disabled = EcsDisabled; static const flecs::entity_t DisabledIntern = EcsDisabledIntern; static const flecs::entity_t Inactive = EcsInactive; static const flecs::entity_t OnDemand = EcsOnDemand; static const flecs::entity_t Monitor = EcsMonitor; static const flecs::entity_t Pipeline = EcsPipeline; /* Trigger tags */ static const flecs::entity_t OnAdd = EcsOnAdd; static const flecs::entity_t OnRemove = EcsOnRemove; static const flecs::entity_t OnSet = EcsOnSet; static const flecs::entity_t UnSet = EcsUnSet; /* Builtin pipeline tags */ static const flecs::entity_t PreFrame = EcsPreFrame; static const flecs::entity_t OnLoad = EcsOnLoad; static const flecs::entity_t PostLoad = EcsPostLoad; static const flecs::entity_t PreUpdate = EcsPreUpdate; static const flecs::entity_t OnUpdate = EcsOnUpdate; static const flecs::entity_t OnValidate = EcsOnValidate; static const flecs::entity_t PostUpdate = EcsPostUpdate; static const flecs::entity_t PreStore = EcsPreStore; static const flecs::entity_t OnStore = EcsOnStore; static const flecs::entity_t PostFrame = EcsPostFrame; /** Builtin roles */ static const flecs::entity_t Pair = ECS_PAIR; static const flecs::entity_t Switch = ECS_SWITCH; static const flecs::entity_t Case = ECS_CASE; static const flecs::entity_t Owned = ECS_OWNED; /* Builtin entity ids */ static const flecs::entity_t Flecs = EcsFlecs; static const flecs::entity_t FlecsCore = EcsFlecsCore; static const flecs::entity_t World = EcsWorld; /* Ids used by rule solver */ static const flecs::entity_t Wildcard = EcsWildcard; static const flecs::entity_t This = EcsThis; static const flecs::entity_t Transitive = EcsTransitive; static const flecs::entity_t Final = EcsFinal; static const flecs::entity_t Tag = EcsTag; /* Builtin relationships */ static const flecs::entity_t IsA = EcsIsA; static const flecs::entity_t ChildOf = EcsChildOf; /* Builtin identifiers */ static const flecs::entity_t Name = EcsName; static const flecs::entity_t Symbol = EcsSymbol; /* Cleanup rules */ static const flecs::entity_t OnDelete = EcsOnDelete; static const flecs::entity_t OnDeleteObject = EcsOnDeleteObject; static const flecs::entity_t Remove = EcsRemove; static const flecs::entity_t Delete = EcsDelete; static const flecs::entity_t Throw = EcsThrow; } //////////////////////////////////////////////////////////////////////////////// //// Flecs STL (FTL?) //// Minimalistic utilities that allow for STL like functionality without having //// to depend on the actual STL. //////////////////////////////////////////////////////////////////////////////// // Macros so that C++ new calls can allocate using ecs_os_api memory allocation functions // Rationale: // - Using macros here instead of a templated function bc clients might override ecs_os_malloc // to contain extra debug info like source tracking location. Using a template function // in that scenario would collapse all source location into said function vs. the // actual call site // - FLECS_PLACEMENT_NEW(): exists to remove any naked new calls/make it easy to identify any regressions // by grepping for new/delete #define FLECS_PLACEMENT_NEW(_ptr, _type) ::new(flecs::_::placement_new_tag, _ptr) _type #define FLECS_NEW(_type) FLECS_PLACEMENT_NEW(ecs_os_malloc(sizeof(_type)), _type) #define FLECS_DELETE(_ptr) \ do { \ if (_ptr) { \ flecs::_::destruct_obj(_ptr); \ ecs_os_free(_ptr); \ } \ } while (false) namespace flecs { namespace _ { // Dummy Placement new tag to disambiguate from any other operator new overrides struct placement_new_tag_t{}; constexpr placement_new_tag_t placement_new_tag{}; template inline void destruct_obj(Ty* _ptr) { _ptr->~Ty(); } template inline void free_obj(Ty* _ptr) { if (_ptr) { destruct_obj(_ptr); ecs_os_free(_ptr); } } } // namespace _ } // namespace flecs inline void* operator new(size_t, flecs::_::placement_new_tag_t, void* _ptr) noexcept { return _ptr; } inline void operator delete(void*, flecs::_::placement_new_tag_t, void*) noexcept { } namespace flecs { // C++11/C++14 convenience template replacements template using conditional_t = typename std::conditional::type; template using decay_t = typename std::decay::type; template using enable_if_t = typename std::enable_if::type; template using remove_pointer_t = typename std::remove_pointer::type; template using remove_reference_t = typename std::remove_reference::type; using std::is_base_of; using std::is_empty; using std::is_const; using std::is_pointer; using std::is_reference; using std::is_volatile; using std::is_same; // Apply cv modifiers from source type to destination type // (from: https://stackoverflow.com/questions/52559336/add-const-to-type-if-template-arg-is-const) template using transcribe_const_t = conditional_t::value, Dst const, Dst>; template using transcribe_volatile_t = conditional_t::value, Dst volatile, Dst>; template using transcribe_cv_t = transcribe_const_t< Src, transcribe_volatile_t< Src, Dst> >; // More convenience templates. The if_*_t templates use int as default type // instead of void. This enables writing code that's a bit less cluttered when // the templates are used in a template declaration: // // enable_if_t* = nullptr // vs: // if_t = 0 template using if_t = enable_if_t; template using if_not_t = enable_if_t; // String handling class string_view; // This removes dependencies on std::string (and therefore STL) and allows the // API to return allocated strings without incurring additional allocations when // wrapping in an std::string. class string { public: explicit string() : m_str(nullptr) , m_const_str("") , m_length(0) { } explicit string(char *str) : m_str(str) , m_const_str(str ? str : "") , m_length(str ? ecs_os_strlen(str) : 0) { } ~string() { // If flecs is included in a binary but is not used, it is possible that // the OS API is not initialized. Calling ecs_os_free in that case could // crash the application during exit. However, if a string has been set // flecs has been used, and OS API should have been initialized. if (m_str) { ecs_os_free(m_str); } } string(string&& str) { ecs_os_free(m_str); m_str = str.m_str; m_const_str = str.m_const_str; m_length = str.m_length; str.m_str = nullptr; } operator const char*() const { return m_const_str; } string& operator=(string&& str) { ecs_os_free(m_str); m_str = str.m_str; m_const_str = str.m_const_str; m_length = str.m_length; str.m_str = nullptr; return *this; } // Ban implicit copies/allocations string& operator=(const string& str) = delete; string(const string& str) = delete; bool operator==(const flecs::string& str) const { if (str.m_const_str == m_const_str) { return true; } if (!m_const_str || !str.m_const_str) { return false; } if (str.m_length != m_length) { return false; } return ecs_os_strcmp(str, m_const_str) == 0; } bool operator!=(const flecs::string& str) const { return !(*this == str); } bool operator==(const char *str) const { if (m_const_str == str) { return true; } if (!m_const_str || !str) { return false; } return ecs_os_strcmp(str, m_const_str) == 0; } bool operator!=(const char *str) const { return !(*this == str); } const char* c_str() const { return m_const_str; } std::size_t length() { return static_cast(m_length); } std::size_t size() { return length(); } void clear() { ecs_os_free(m_str); m_str = nullptr; m_const_str = nullptr; } protected: // Must be constructed through string_view. This allows for using the string // class for both owned and non-owned strings, which can reduce allocations // when code conditionally should store a literal or an owned string. // Making this constructor private forces the code to explicitly create a // string_view which emphasizes that the string won't be freed by the class. string(const char *str) : m_str(nullptr) , m_const_str(str ? str : "") , m_length(str ? ecs_os_strlen(str) : 0) { } char *m_str = nullptr; const char *m_const_str; ecs_size_t m_length; }; // For consistency, the API returns a string_view where it could have returned // a const char*, so an application won't have to think about whether to call // c_str() or not. The string_view is a thin wrapper around a string that forces // the API to indicate explicitly when a string is owned or not. class string_view : public string { public: explicit string_view(const char *str) : string(str) { } }; // Wrapper around ecs_strbuf_t that provides a simple stringstream like API. class stringstream { public: explicit stringstream() : m_buf({}) { } ~stringstream() { ecs_strbuf_reset(&m_buf); } stringstream(stringstream&& str) { ecs_strbuf_reset(&m_buf); m_buf = str.m_buf; str.m_buf = {}; } stringstream& operator=(stringstream&& str) { ecs_strbuf_reset(&m_buf); m_buf = str.m_buf; str.m_buf = {}; return *this; } // Ban implicit copies/allocations stringstream& operator=(const stringstream& str) = delete; stringstream(const stringstream& str) = delete; stringstream& operator<<(const char* str) { ecs_strbuf_appendstr(&m_buf, str); return *this; } flecs::string str() { return flecs::string(ecs_strbuf_get(&m_buf)); } private: ecs_strbuf_t m_buf; }; // Array class. Simple std::array like utility that is mostly there to aid // template code, where the expanded array size would be 0. template class array_iterator { public: explicit array_iterator(T* value, int index) { m_value = value; m_index = index; } bool operator!=(array_iterator const& other) const { return m_index != other.m_index; } T & operator*() const { return m_value[m_index]; } array_iterator& operator++() { ++m_index; return *this; } private: T* m_value; int m_index; }; template class array { }; template class array > { public: array() {}; array(const T (&elems)[Size]) { int i = 0; for (auto it = this->begin(); it != this->end(); ++ it) { *it = elems[i ++]; } } T& operator[](size_t index) { return m_array[index]; } array_iterator begin() { return array_iterator(m_array, 0); } array_iterator end() { return array_iterator(m_array, Size); } size_t size() { return Size; } T* ptr() { return m_array; } private: T m_array[Size]; }; // Specialized class for zero-sized array template class array> { public: array() {}; array(const T* (&elems)) { (void)elems; } T operator[](size_t index) { abort(); (void)index; return T(); } array_iterator begin() { return array_iterator(nullptr, 0); } array_iterator end() { return array_iterator(nullptr, 0); } size_t size() { return 0; } T* ptr() { return NULL; } }; namespace _ { // Utility to prevent static assert from immediately triggering template struct always_false { static const bool value = false; }; } // namespace _ } // namespace flecs namespace flecs { namespace _ { struct pair_base { }; } // _ // Type that represents a pair and can encapsulate a temporary value template struct pair : _::pair_base { // Traits used to deconstruct the pair // The actual type of the pair is determined by which type of the pair is // empty. If both types are empty or not empty, the pair assumes the type // of the relation. using type = conditional_t::value || is_empty::value, R, O>; using relation = R; using object = O; pair(type& v) : ref_(v) { } // This allows the class to be used as a temporary object pair(const type& v) : ref_(const_cast(v)) { } operator type&() { return ref_; } operator const Type&() const { return ref_; } type* operator->() { return &ref_; } const type* operator->() const { return &ref_; } type& operator*() { return &ref_; } const type& operator*() const { return ref_; } private: type& ref_; }; template ::value> = 0> using pair_object = pair; // Utilities to test if type is a pair template struct is_pair { static constexpr bool value = is_base_of<_::pair_base, remove_reference_t >::value; }; // Get actual type, relation or object from pair while preserving cv qualifiers. template using pair_relation_t = transcribe_cv_t, typename remove_reference_t

::relation>; template using pair_object_t = transcribe_cv_t, typename remove_reference_t

::object>; template using pair_type_t = transcribe_cv_t, typename remove_reference_t

::type>; // Get actual type from a regular type or pair template struct actual_type; template struct actual_type::value >> { using type = T; }; template struct actual_type::value >> { using type = pair_type_t; }; template using actual_type_t = typename actual_type::type; // Get type without const, *, & template struct base_type { using type = remove_pointer_t< decay_t< actual_type_t > >; }; template using base_type_t = typename base_type::type; // Get type without *, & (retains const which is useful for function args) template struct base_arg_type { using type = remove_pointer_t< remove_reference_t< actual_type_t > >; }; template using base_arg_type_t = typename base_arg_type::type; // Test if type is the same as its actual type template struct is_actual { static constexpr bool value = std::is_same >::value; }; } // flecs // Neat utility to inspect arguments & returntype of a function type // Code from: https://stackoverflow.com/questions/27024238/c-template-mechanism-to-get-the-number-of-function-arguments-which-would-work namespace flecs { namespace _ { template struct arg_list { }; // Base type that contains the traits template struct function_traits_defs { static constexpr bool is_callable = true; static constexpr size_t arity = sizeof...(Args); using return_type = ReturnType; using args = arg_list; }; // Primary template for function_traits_impl template struct function_traits_impl { static constexpr bool is_callable = false; }; // Template specializations for the different kinds of function types (whew) template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; template struct function_traits_impl : function_traits_defs {}; // Primary template for function_traits_no_cv. If T is not a function, the // compiler will attempt to instantiate this template and fail, because its base // is undefined. template struct function_traits_no_cv : function_traits_impl {}; // Specialized template for function types template struct function_traits_no_cv : function_traits_impl {}; // Front facing template that decays T before ripping it apart. template struct function_traits : function_traits_no_cv< decay_t > {}; } // _ template struct is_callable { static constexpr bool value = _::function_traits::is_callable; }; template struct arity { static constexpr int value = _::function_traits::arity; }; template using return_type_t = typename _::function_traits::return_type; template using arg_list_t = typename _::function_traits::args; template struct first_arg_impl; template struct first_arg_impl > { using type = T; }; template struct first_arg { using type = typename first_arg_impl>::type; }; template using first_arg_t = typename first_arg::type; } // flecs namespace flecs { namespace _ { inline void ecs_ctor_illegal(ecs_world_t* w, ecs_entity_t id, const ecs_entity_t*, void *, size_t, int32_t, void*) { char *path = ecs_get_path_w_sep(w, 0, id, "::", "::"); ecs_abort(ECS_INVALID_OPERATION, "cannnot default construct %s, add %s::%s() or use emplace", path, path, ecs_get_name(w, id)); ecs_os_free(path); } inline void ecs_dtor_illegal(ecs_world_t* w, ecs_entity_t id, const ecs_entity_t*, void *, size_t, int32_t, void*) { char *path = ecs_get_path_w_sep(w, 0, id, "::", "::"); ecs_abort(ECS_INVALID_OPERATION, "cannnot destruct %s, add ~%s::%s()", path, path, ecs_get_name(w, id)); ecs_os_free(path); } inline void ecs_copy_illegal(ecs_world_t* w, ecs_entity_t id, const ecs_entity_t*, const ecs_entity_t*, void *, const void *, size_t, int32_t, void*) { char *path = ecs_get_path_w_sep(w, 0, id, "::", "::"); ecs_abort(ECS_INVALID_OPERATION, "cannnot copy assign %s, add %s& %s::operator =(const %s&)", path, ecs_get_name(w, id), path, ecs_get_name(w, id), ecs_get_name(w, id)); ecs_os_free(path); } inline void ecs_move_illegal(ecs_world_t* w, ecs_entity_t id, const ecs_entity_t*, const ecs_entity_t*, void *, void *, size_t, int32_t, void*) { char *path = ecs_get_path_w_sep(w, 0, id, "::", "::"); ecs_abort(ECS_INVALID_OPERATION, "cannnot move assign %s, add %s& %s::operator =(%s&&)", path, ecs_get_name(w, id), path, ecs_get_name(w, id), ecs_get_name(w, id)); ecs_os_free(path); } inline void ecs_copy_ctor_illegal(ecs_world_t* w, ecs_entity_t id, const EcsComponentLifecycle*, const ecs_entity_t*, const ecs_entity_t*, void *, const void *, size_t, int32_t, void*) { char *path = ecs_get_path_w_sep(w, 0, id, "::", "::"); ecs_abort(ECS_INVALID_OPERATION, "cannnot copy construct %s, add %s::%s(const %s&)", path, path, ecs_get_name(w, id), ecs_get_name(w, id)); ecs_os_free(path); } inline void ecs_move_ctor_illegal(ecs_world_t* w, ecs_entity_t id, const EcsComponentLifecycle*, const ecs_entity_t*, const ecs_entity_t*, void *, void *, size_t, int32_t, void*) { char *path = ecs_get_path_w_sep(w, 0, id, "::", "::"); ecs_abort(ECS_INVALID_OPERATION, "cannnot move construct %s, add %s::%s(%s&&)", path, path, ecs_get_name(w, id), ecs_get_name(w, id)); ecs_os_free(path); } // T() // Can't coexist with T(flecs::entity) or T(flecs::world, flecs::entity) template void ctor_impl( ecs_world_t*, ecs_entity_t, const ecs_entity_t*, void *ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *arr = static_cast(ptr); for (int i = 0; i < count; i ++) { FLECS_PLACEMENT_NEW(&arr[i], T); } } // T(flecs::world, flecs::entity) template void ctor_world_entity_impl( ecs_world_t* world, ecs_entity_t, const ecs_entity_t* ids, void *ptr, size_t size, int32_t count, void*); // ~T() template void dtor_impl( ecs_world_t*, ecs_entity_t, const ecs_entity_t*, void *ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *arr = static_cast(ptr); for (int i = 0; i < count; i ++) { arr[i].~T(); } } // T& operator=(const T&) template void copy_impl( ecs_world_t*, ecs_entity_t, const ecs_entity_t*, const ecs_entity_t*, void *dst_ptr, const void *src_ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *dst_arr = static_cast(dst_ptr); const T *src_arr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { dst_arr[i] = src_arr[i]; } } // T& operator=(T&&) template void move_impl( ecs_world_t*, ecs_entity_t, const ecs_entity_t*, const ecs_entity_t*, void *dst_ptr, void *src_ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *dst_arr = static_cast(dst_ptr); T *src_arr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { dst_arr[i] = std::move(src_arr[i]); } } // T(T&) template void copy_ctor_impl( ecs_world_t*, ecs_entity_t, const EcsComponentLifecycle*, const ecs_entity_t*, const ecs_entity_t*, void *dst_ptr, const void *src_ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *dst_arr = static_cast(dst_ptr); const T *src_arr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { FLECS_PLACEMENT_NEW(&dst_arr[i], T(src_arr[i])); } } // T(T&&) template void move_ctor_impl( ecs_world_t*, ecs_entity_t, const EcsComponentLifecycle*, const ecs_entity_t*, const ecs_entity_t*, void *dst_ptr, void *src_ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *dst_arr = static_cast(dst_ptr); T *src_arr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { FLECS_PLACEMENT_NEW(&dst_arr[i], T(std::move(src_arr[i]))); } } // T(T&&), ~T() // Typically used when moving to a new table, and removing from the old table template void ctor_move_dtor_impl( ecs_world_t*, ecs_entity_t, const EcsComponentLifecycle*, const ecs_entity_t*, const ecs_entity_t*, void *dst_ptr, void *src_ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *dst_arr = static_cast(dst_ptr); T *src_arr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { FLECS_PLACEMENT_NEW(&dst_arr[i], T(std::move(src_arr[i]))); src_arr[i].~T(); } } // Move assign + dtor (non-trivial move assigmnment) // Typically used when moving a component to a deleted component template ::value > = 0> void move_dtor_impl( ecs_world_t*, ecs_entity_t, const EcsComponentLifecycle*, const ecs_entity_t*, const ecs_entity_t*, void *dst_ptr, void *src_ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *dst_arr = static_cast(dst_ptr); T *src_arr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { // Move assignment should free dst & assign dst to src dst_arr[i] = std::move(src_arr[i]); // Destruct src. Move should have left object in a state where it no // longer holds resources, but it still needs to be destructed. src_arr[i].~T(); } } // Move assign + dtor (trivial move assigmnment) // Typically used when moving a component to a deleted component template ::value > = 0> void move_dtor_impl( ecs_world_t*, ecs_entity_t, const EcsComponentLifecycle*, const ecs_entity_t*, const ecs_entity_t*, void *dst_ptr, void *src_ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *dst_arr = static_cast(dst_ptr); T *src_arr = static_cast(src_ptr); for (int i = 0; i < count; i ++) { // Cleanup resources of dst dst_arr[i].~T(); // Copy src to dst dst_arr[i] = std::move(src_arr[i]); // No need to destruct src. Since this is a trivial move the code // should be agnostic to the address of the component which means we // can pretend nothing got destructed. } } } // _ // Trait to test if type has flecs constructor template struct has_flecs_ctor { static constexpr bool value = std::is_constructible, flecs::world&, flecs::entity>::value; }; // Trait to test if type is constructible by flecs template struct is_flecs_constructible { static constexpr bool value = std::is_default_constructible>::value || std::is_constructible, flecs::world&, flecs::entity>::value; }; // Trait to test if type has a self constructor (flecs::entity, Args...) template struct is_self_constructible { static constexpr bool value = std::is_constructible, flecs::entity, Args...>::value; }; namespace _ { // Trivially constructible template ::value > = 0> ecs_xtor_t ctor() { return nullptr; } // Not constructible by flecs template ::value && ! has_flecs_ctor::value > = 0> ecs_xtor_t ctor() { return ecs_ctor_illegal; } // Default constructible template ::value && std::is_default_constructible::value && ! has_flecs_ctor::value > = 0> ecs_xtor_t ctor() { return ctor_impl; } // Flecs constructible: T(flecs::world, flecs::entity) template ::value > = 0> ecs_xtor_t ctor() { return ctor_world_entity_impl; } // No dtor template ::value > = 0> ecs_xtor_t dtor() { return nullptr; } // Dtor template ::value && ! std::is_trivially_destructible::value > = 0> ecs_xtor_t dtor() { return dtor_impl; } // Assert when the type cannot be destructed template ::value > = 0> ecs_xtor_t dtor() { flecs_static_assert(always_false::value, "component type must be destructible"); return ecs_dtor_illegal; } // Trivially copyable template ::value > = 0> ecs_copy_t copy() { return nullptr; } // Not copyable template ::value && ! std::is_copy_assignable::value > = 0> ecs_copy_t copy() { return ecs_copy_illegal; } // Copy assignment template ::value && ! std::is_trivially_copyable::value > = 0> ecs_copy_t copy() { return copy_impl; } // Trivially move assignable template ::value > = 0> ecs_move_t move() { return nullptr; } // Component types must be move assignable template ::value > = 0> ecs_move_t move() { flecs_static_assert(always_false::value, "component type must be move assignable"); return ecs_move_illegal; } // Move assignment template ::value && ! std::is_trivially_move_assignable::value > = 0> ecs_move_t move() { return move_impl; } // Trivially copy constructible template ::value > = 0> ecs_copy_ctor_t copy_ctor() { return nullptr; } // No copy ctor template ::value > = 0> ecs_copy_ctor_t copy_ctor() { return ecs_copy_ctor_illegal; } // Copy ctor template ::value && ! std::is_trivially_copy_constructible::value > = 0> ecs_copy_ctor_t copy_ctor() { return copy_ctor_impl; } // Trivially move constructible template ::value > = 0> ecs_move_ctor_t move_ctor() { return nullptr; } // Component types must be move constructible template ::value > = 0> ecs_move_ctor_t move_ctor() { flecs_static_assert(always_false::value, "component type must be move constructible"); return ecs_move_ctor_illegal; } // Move ctor template ::value && ! std::is_trivially_move_constructible::value > = 0> ecs_move_ctor_t move_ctor() { return move_ctor_impl; } // Trivial merge (move assign + dtor) template ::value && std::is_trivially_destructible::value > = 0> ecs_move_ctor_t ctor_move_dtor() { return nullptr; } // Component types must be move constructible and destructible template ::value || ! std::is_destructible::value > = 0> ecs_move_ctor_t ctor_move_dtor() { flecs_static_assert(always_false::value, "component type must be move constructible and destructible"); return ecs_move_ctor_illegal; } // Merge ctor + dtor template ::value && std::is_trivially_destructible::value) && std::is_move_constructible::value && std::is_destructible::value > = 0> ecs_move_ctor_t ctor_move_dtor() { return ctor_move_dtor_impl; } // Trivial merge (move assign + dtor) template ::value && std::is_trivially_destructible::value > = 0> ecs_move_ctor_t move_dtor() { return nullptr; } // Component types must be move constructible and destructible template ::value || ! std::is_destructible::value > = 0> ecs_move_ctor_t move_dtor() { flecs_static_assert(always_false::value, "component type must be move constructible and destructible"); return ecs_move_ctor_illegal; } // Merge assign + dtor template ::value && std::is_trivially_destructible::value) && std::is_move_assignable::value && std::is_destructible::value > = 0> ecs_move_ctor_t move_dtor() { return move_dtor_impl; } } // _ } // flecs namespace flecs { /** 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: static_assert(std::is_empty() == false, "invalid type for column, cannot iterate empty type"); /** 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 first element of component array. * This operator is typically used when the column is shared. * * @return Pointer 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; } /** Return whether component is owned. * If the column is shared, this method returns true. * * @return True if component is shared, false if component is owned. */ bool is_owned() const { return !m_is_shared; } protected: T* m_array; size_t m_count; bool m_is_shared; }; //////////////////////////////////////////////////////////////////////////////// namespace _ { //////////////////////////////////////////////////////////////////////////////// /** 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 _ } // namespace flecs #ifdef FLECS_DEPRECATED namespace flecs { /* Deprecated functions */ template class iter_deprecated { public: ECS_DEPRECATED("use term_count(int32_t)") int32_t column_count() const { return base()->term_count(); } ECS_DEPRECATED("use term_size(int32_t)") size_t column_size(int32_t col) const { return base()->term_size(col); } ECS_DEPRECATED("use is_owned(int32_t)") bool is_shared(int32_t col) const { return !base()->is_owned(col); } ECS_DEPRECATED("use term_source(int32_t)") flecs::entity column_source(int32_t col) const; ECS_DEPRECATED("use term_id(int32_t)") flecs::entity column_entity(int32_t col) const; ECS_DEPRECATED("no replacement") flecs::type column_type(int32_t col) const; ECS_DEPRECATED("use type()") type table_type() const; template ::value > = 0> ECS_DEPRECATED("use term(int32_t)") flecs::column column(int32_t col) const { return base()->template term(col); } template ::value > = 0> ECS_DEPRECATED("use term(int32_t)") flecs::column column(int32_t col) const { ecs_assert(!ecs_is_readonly(iter(), col), ECS_COLUMN_ACCESS_VIOLATION, NULL); return base()->template term(col); } ECS_DEPRECATED("use term(int32_t)") flecs::unsafe_column column(int32_t col) const { return base()->term(col); } template ECS_DEPRECATED("use owned(int32_t)") flecs::column owned(int32_t col) const { return base()->template owned(col); } template ECS_DEPRECATED("use shared(int32_t)") const T& shared(int32_t col) const { return base()->template shared(col); } template ::value > = 0> ECS_DEPRECATED("no replacement") T& element(int32_t col, int32_t row) const { return base()->template get_element(col, row); } template ::value > = 0> ECS_DEPRECATED("no replacement") T& element(int32_t col, int32_t row) const { ecs_assert(!ecs_is_readonly(iter(), col), ECS_COLUMN_ACCESS_VIOLATION, NULL); return base()->template get_element(col, row); } private: const Base* base() const { return static_cast(this); } const flecs::iter_t* iter() const { return base()->c_ptr(); } }; } #else namespace flecs { template class iter_deprecated { }; } #endif namespace flecs { //////////////////////////////////////////////////////////////////////////////// /** Class that enables iterating over table columns. */ class iter : public iter_deprecated { 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; /** Obtain pointer to C iterator object */ const flecs::iter_t* c_ptr() const { return m_iter; } /** Number of entities to iterate over. */ size_t count() const { return static_cast(m_iter->count); } /** 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; } /** Obtain type of the entities being iterated over. */ flecs::type type() const; /** Is current type a module or does it contain module contents? */ bool has_module() const { return ecs_table_has_module(ecs_iter_table(m_iter)); } /** Access self. * 'self' is an entity that can be associated with a trigger, observer or * system when they are created. */ flecs::entity self() const; /** Access ctx. * ctx contains the context pointer assigned to a system. */ void* ctx() { return m_iter->ctx; } /** Access param. * param contains the pointer passed to the param argument of system::run */ void* param() { return m_iter->param; } /** Obtain mutable handle to entity being iterated over. * * @param row Row being iterated over. */ flecs::entity entity(size_t row) const; /** Obtain the total number of inactive tables the query is matched with. */ int32_t inactive_table_count() const { return m_iter->inactive_table_count; } /** Returns whether term is owned. * * @param index The term index. */ bool is_owned(int32_t index) const { return ecs_term_is_owned(m_iter, index); } /** Returns whether term is set. * * @param index The term index. */ bool is_set(int32_t index) const { return ecs_term_is_set(m_iter, index); } /** Returns whether term is readonly. * * @param index The term index. */ bool is_readonly(int32_t index) const { return ecs_term_is_readonly(m_iter, index); } /** Number of terms in iteator. */ int32_t term_count() const { return m_iter->column_count; } /** Size of term data type. * * @param index The term id. */ size_t term_size(int32_t index) const { return ecs_term_size(m_iter, index); } /** Obtain term source (0 if self) * * @param index The term index. */ flecs::entity term_source(int32_t index) const; /** Obtain component/tag entity of term. * * @param index The term index. */ flecs::entity term_id(int32_t index) const; /** Obtain term with const type. * If the specified term index does not match with the provided type, the * function will assert. * * @tparam T Type of the term. * @param index The term index. * @return The term data. */ template , typename std::enable_if::value, void>::type* = nullptr> flecs::column term(int32_t index) const { return get_term(index); } /** Obtain term with non-const type. * If the specified term id does not match with the provided type or if * the term is readonly, the function will assert. * * @tparam T Type of the term. * @param index The term index. * @return The term data. */ template , typename std::enable_if< std::is_const::value == false, void>::type* = nullptr> flecs::column term(int32_t index) const { ecs_assert(!ecs_term_is_readonly(m_iter, index), ECS_COLUMN_ACCESS_VIOLATION, NULL); return get_term(index); } /** Obtain unsafe term. * Unsafe terms are required when a system does not know at compile time * which component will be passed to it. * * @param index The term index. */ flecs::unsafe_column term(int32_t index) const { return get_unsafe_term(index); } /** Obtain owned term. * Same as iter::term, but ensures that term is owned. * * @tparam Type of the term. * @param index The term index. * @return The term data. */ template > flecs::column term_owned(int32_t index) const { ecs_assert(!!ecs_is_owned(m_iter, index), ECS_COLUMN_IS_SHARED, NULL); return this->term(index); } /** Obtain shared term. * Same as iter::term, but ensures that term is shared. * * @tparam Type of the term. * @param index The term index. * @return The component term. */ template > const T& term_shared(int32_t index) const { ecs_assert( ecs_term_id(m_iter, index) == _::cpp_type::id(m_iter->world), ECS_COLUMN_TYPE_MISMATCH, NULL); ecs_assert(!ecs_term_is_owned(m_iter, index), ECS_COLUMN_IS_NOT_SHARED, NULL); return *static_cast(ecs_term_w_size(m_iter, sizeof(A), index)); } /** Obtain the total number of tables the iterator will iterate over. */ int32_t table_count() const { return m_iter->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_iter_column_w_size(m_iter, 0, 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 col = ecs_iter_find_column(m_iter, _::cpp_type::id()); ecs_assert(col != -1, ECS_INVALID_PARAMETER, NULL); return flecs::column(static_cast(ecs_iter_column_w_size(m_iter, sizeof(A), col)), static_cast(m_iter->count), false); } template flecs::column table_column(flecs::id_t obj) const { auto col = ecs_iter_find_column(m_iter, ecs_pair(_::cpp_type::id(), obj)); ecs_assert(col != -1, ECS_INVALID_PARAMETER, NULL); return flecs::column(static_cast(ecs_iter_column_w_size(m_iter, sizeof(T), col)), static_cast(m_iter->count), false); } private: /* Get term, check if correct type is used */ template > flecs::column get_term(int32_t index) const { #ifndef NDEBUG ecs_entity_t term_id = ecs_term_id(m_iter, index); ecs_assert(term_id & ECS_PAIR || term_id & ECS_SWITCH || term_id & ECS_CASE || term_id == _::cpp_type::id(m_iter->world), ECS_COLUMN_TYPE_MISMATCH, NULL); #endif size_t count; bool is_shared = !ecs_term_is_owned(m_iter, index); /* 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_term_w_size(m_iter, sizeof(A), index)), count, is_shared); } flecs::unsafe_column get_unsafe_term(int32_t index) const { size_t count; size_t size = ecs_term_size(m_iter, index); bool is_shared = !ecs_term_is_owned(m_iter, index); /* 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_term_w_size(m_iter, 0, index), size, count, is_shared); } const flecs::iter_t *m_iter; std::size_t m_begin; std::size_t m_end; }; } // namespace flecs namespace flecs { /** Static helper functions to assign a component value */ // set(T&&), T = constructible template ::value > = 0> inline void set(world_t *world, entity_t entity, T&& value, ecs_id_t id) { ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); T& dst = *static_cast(ecs_get_mut_id(world, entity, id, NULL)); dst = std::move(value); ecs_modified_id(world, entity, id); } // set(const T&), T = constructible template ::value > = 0> inline void set(world_t *world, entity_t entity, const T& value, ecs_id_t id) { ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); T& dst = *static_cast(ecs_get_mut_id(world, entity, id, NULL)); dst = value; ecs_modified_id(world, entity, id); } // set(T&&), T = not constructible template ::value > = 0> inline void set(world_t *world, entity_t entity, T&& value, ecs_id_t id) { ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); bool is_new = false; T& dst = *static_cast(ecs_get_mut_id(world, entity, id, &is_new)); /* If type is not constructible get_mut should assert on new values */ ecs_assert(!is_new, ECS_INTERNAL_ERROR, NULL); dst = std::move(value); ecs_modified_id(world, entity, id); } // set(const T&), T = not constructible template ::value > = 0> inline void set(world_t *world, id_t entity, const T& value, id_t id) { ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); bool is_new = false; T& dst = *static_cast(ecs_get_mut_id(world, entity, id, &is_new)); /* If type is not constructible get_mut should assert on new values */ ecs_assert(!is_new, ECS_INTERNAL_ERROR, NULL); dst = value; ecs_modified_id(world, entity, id); } // emplace for T(Args...) template , Args...>::value || std::is_default_constructible>::value > = 0> inline void emplace(world_t *world, id_t entity, Args&&... args) { id_t id = _::cpp_type::id(world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); T& dst = *static_cast(ecs_emplace_id(world, entity, id)); FLECS_PLACEMENT_NEW(&dst, T{std::forward(args)...}); ecs_modified_id(world, entity, id); } // emplace for T(flecs::entity, Args...) template , flecs::entity, Args...>::value > = 0> inline void emplace(world_t *world, id_t entity, Args&&... args); // set(T&&) template inline void set(world_t *world, entity_t entity, A&& value) { id_t id = _::cpp_type::id(world); flecs::set(world, entity, std::forward(value), id); } // set(const T&) template inline void set(world_t *world, entity_t entity, const A& value) { id_t id = _::cpp_type::id(world); flecs::set(world, entity, value, id); } /** 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. */ explicit 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. */ explicit 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; } /* Implicit conversion to world_t* */ operator world_t*() const { return m_world; } /** 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 && ecs_stage_is_async(m_world)) { ecs_async_stage_free(m_world); } else 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); } void set_pipeline(const flecs::pipeline& pip) const; /** Progress world, run all systems. * * @param delta_time Custom delta_time. If 0 is provided, Flecs will automatically measure delta_time. */ bool progress(FLECS_FLOAT delta_time = 0.0) const { return ecs_progress(m_world, delta_time); } /** Get last delta_time. */ FLECS_FLOAT delta_time() const { const ecs_world_info_t *stats = ecs_get_world_info(m_world); return stats->delta_time; } /** Signal application should quit. * After calling this operation, the next call to progress() returns false. */ void quit() { ecs_quit(m_world); } /** Test if quit() has been called. */ bool should_quit() { return ecs_should_quit(m_world); } /** Get id from a type. */ template flecs::id id() const; /** Id factory. */ template flecs::id id(Args&&... args) const; /** Get pair id from relation, object */ template flecs::id pair() const; /** Get pair id from relation, object */ template flecs::id pair(entity_t o) const; /** Get pair id from relation, object */ flecs::id pair(entity_t r, entity_t o) const; /** Begin frame. * When an application does not use progress() to control the main loop, it * can still use Flecs features such as FPS limiting and time measurements. * This operation needs to be invoked whenever a new frame is about to get * processed. * * Calls to frame_begin must always be followed by frame_end. * * The function accepts a delta_time parameter, which will get passed to * systems. This value is also used to compute the amount of time the * function needs to sleep to ensure it does not exceed the target_fps, when * it is set. When 0 is provided for delta_time, the time will be measured. * * This function should only be ran from the main thread. * * @param delta_time Time elapsed since the last frame. * @return The provided delta_time, or measured time if 0 was provided. */ FLECS_FLOAT frame_begin(float delta_time = 0) { return ecs_frame_begin(m_world, delta_time); } /** End frame. * This operation must be called at the end of the frame, and always after * ecs_frame_begin. * * This function should only be ran from the main thread. */ void frame_end() { ecs_frame_end(m_world); } /** Begin staging. * When an application does not use ecs_progress to control the main loop, it * can still use Flecs features such as the defer queue. When an application * needs to stage changes, it needs to call this function after ecs_frame_begin. * A call to ecs_staging_begin must be followed by a call to ecs_staging_end. * * 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. * * While the world is in staging mode, no structural changes (add/remove/...) * can be made to the world itself. Operations must be executed on a stage * instead (see ecs_get_stage). * * This function should only be ran from the main thread. * * @return Whether world is currently staged. */ bool staging_begin() { return ecs_staging_begin(m_world); } /** End staging. * Leaves staging mode. After this operation the world may be directly mutated * again. By default this operation also merges data back into the world, unless * automerging was disabled explicitly. * * This function should only be ran from the main thread. */ void staging_end() { ecs_staging_end(m_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. */ 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); } /** Test whether deferring is enabled. */ bool is_deferred() { return ecs_is_deferred(m_world); } /** Configure world to have N stages. * This initializes N stages, which allows applications to defer operations to * multiple isolated defer queues. This is typically used for applications with * multiple threads, where each thread gets its own queue, and commands are * merged when threads are synchronized. * * Note that set_threads() already creates the appropriate number of stages. * The set_stages() operation is useful for applications that want to manage * their own stages and/or threads. * * @param stages The number of stages. */ void set_stages(int32_t stages) const { ecs_set_stages(m_world, stages); } /** Get number of configured stages. * Return number of stages set by set_stages. * * @return The number of stages used for threading. */ int32_t get_stage_count() const { return ecs_get_stage_count(m_world); } /** Get current stage id. * The stage id can be used by an application to learn about which stage it * is using, which typically corresponds with the worker thread id. * * @return The stage id. */ int32_t get_stage_id() const { return ecs_get_stage_id(m_world); } /** Enable/disable automerging for world or stage. * When automerging is enabled, staged data will automatically be merged * with the world when staging ends. This happens at the end of progress(), * at a sync point or when staging_end() is called. * * Applications can exercise more control over when data from a stage is * merged by disabling automerging. This requires an application to * explicitly call merge() on the stage. * * When this function is invoked on the world, it sets all current stages to * the provided value and sets the default for new stages. When this * function is invoked on a stage, automerging is only set for that specific * stage. * * @param automerge Whether to enable or disable automerging. */ void set_automerge(bool automerge) { ecs_set_automerge(m_world, automerge); } /** Merge world or stage. * When automatic merging is disabled, an application can call this * operation on either an individual stage, or on the world which will merge * all stages. This operation may only be called when staging is not enabled * (either after progress() or after staging_end()). * * This operation may be called on an already merged stage or world. */ void merge() { ecs_merge(m_world); } /** Get stage-specific world pointer. * Flecs threads can safely invoke the API as long as they have a private * context to write to, also referred to as the stage. This function returns a * pointer to a stage, disguised as a world pointer. * * Note that this function does not(!) create a new world. It simply wraps the * existing world in a thread-specific context, which the API knows how to * unwrap. The reason the stage is returned as an ecs_world_t is so that it * can be passed transparently to the existing API functions, vs. having to * create a dediated API for threading. * * @param stage_id The index of the stage to retrieve. * @return A thread-specific pointer to the world. */ flecs::world get_stage(int32_t stage_id) const { return flecs::world(ecs_get_stage(m_world, stage_id)); } /** Create asynchronous stage. * An asynchronous stage can be used to asynchronously queue operations for * later merging with the world. An asynchronous stage is similar to a regular * stage, except that it does not allow reading from the world. * * Asynchronous stages are never merged automatically, and must therefore be * manually merged with the ecs_merge function. It is not necessary to call * defer_begin or defer_end before and after enqueuing commands, as an * asynchronous stage unconditionally defers operations. * * The application must ensure that no commands are added to the stage while the * stage is being merged. * * An asynchronous stage must be cleaned up by ecs_async_stage_free. * * @return The stage. */ flecs::world async_stage() const { auto result = flecs::world(ecs_async_stage_new(m_world)); result.m_owned = true; return result; } /** Get actual world. * If the current object points to a stage, this operation will return the * actual world. * * @return The actual world. */ flecs::world get_world() const { /* Safe cast, mutability is checked */ return flecs::world( m_world ? const_cast(ecs_get_world(m_world)) : nullptr); } /** Test whether the current world object is readonly. * This function allows the code to test whether the currently used world * object is readonly or whether it allows for writing. * * @return True if the world or stage is readonly. */ bool is_readonly() const { return ecs_stage_is_readonly(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(int32_t threads) const { ecs_set_threads(m_world, threads); } /** Get number of threads. * * @return Number of configured threads. */ int32_t get_threads() const { return ecs_get_threads(m_world); } /** Get index of current thread. * * @return Unique index for current thread. */ ECS_DEPRECATED("use get_stage_id") int32_t get_thread_index() const { return ecs_get_stage_id(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. */ 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(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, 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); } /** Set current scope. * * @param scope The scope to set. * @return The current scope; */ flecs::entity set_scope(const flecs::entity& scope) const; /** Get current scope. * * @return The current scope. */ flecs::entity get_scope() const; /** Lookup entity by name. * * @param name Entity name. */ flecs::entity lookup(const char *name) const; /** Set singleton component. */ template void set(const T& value) const { flecs::set(m_world, _::cpp_type::id(m_world), value); } template void set(T&& value) const { flecs::set(m_world, _::cpp_type::id(m_world), std::forward(value)); } template void emplace(Args&&... args) const { flecs::emplace(m_world, _::cpp_type::id(m_world), std::forward(args)...); } /** Get mut singleton component. */ template T* get_mut() const; /** Mark singleton component as modified. */ template void modified() const; /** Patch singleton component. */ template void patch(const Func& func) const; /** Get singleton component. */ template const T* get() const; /** Test if world has singleton component. */ template bool has() const; /** Add singleton component. */ template void add() const; /** Remove singleton component. */ template void remove() const; /** Get id for type. */ template entity_t type_id() { return _::cpp_type::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); /** Count entities matching a component. * * @tparam T The component to use for matching. */ template int count() const { return ecs_count_id(m_world, _::cpp_type::id(m_world)); } flecs::filter_iterator begin() const; flecs::filter_iterator end() 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); } /** All entities created in function are created with id. */ template void with(id_t with_id, const Func& func) const { ecs_id_t prev = ecs_set_with(m_world, with_id); func(); ecs_set_with(m_world, prev); } /** All entities created in function are created with type. */ template void with(const Func& func) const { with(this->id(), func); } /** All entities created in function are created with relation. */ template void with(const Func& func) const { with(ecs_pair(this->id(), this->id()), func); } /** All entities created in function are created with relation. */ template void with(id_t object, const Func& func) const { with(ecs_pair(this->id(), object), func); } /** All entities created in function are created with relation. */ template void with(id_t relation, id_t object, const Func& func) const { with(ecs_pair(relation, object), func); } /** All entities created in function are created in scope. All operations * called in function (such as lookup) are relative to scope. */ template void scope(id_t parent, const Func& func) const { ecs_entity_t prev = ecs_set_scope(m_world, parent); func(); ecs_set_scope(m_world, prev); } /** Defer all operations called in function. If the world is already in * deferred mode, do nothing. */ template void defer(const Func& func) const { ecs_defer_begin(m_world); func(); ecs_defer_end(m_world); } /** Iterate over all entities with provided component. * The function parameter must match the following signature: * void(*)(T&) or * void(*)(flecs::entity, T&) */ template void each(Func&& func) const; /** Iterate over all entities with provided (component) id. */ template void each(flecs::id_t term_id, Func&& func) const; /** Iterate over all entities with components in argument list of function. * The function parameter must match the following signature: * void(*)(T&, U&, ...) or * void(*)(flecs::entity, T&, U&, ...) */ template void each(Func&& func) const; /** 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 pipeline. */ template flecs::pipeline pipeline(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 a system from an entity */ flecs::system<> system(flecs::entity e) const; /** Create a system. */ template flecs::system_builder system(Args &&... args) const; /** Create an observer. */ template flecs::observer_builder observer(Args &&... args) const; /** Create a filter. */ template flecs::filter filter(Args &&... args) const; /** Create a filter builder. */ template flecs::filter_builder filter_builder(Args &&... args) const; /** Create a query. */ template flecs::query query(Args &&... args) const; /** Create a query builder. */ template flecs::query_builder query_builder(Args &&... args) const; /** Create a term */ template flecs::term term(Args &&... args) const; /** Create a term for a type */ template flecs::term term(Args &&... args) const; /** Create a term for a pair */ template flecs::term term(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; }; // Downcast utility to make world available to classes in inheritance hierarchy template class world_base { public: template static flecs::world world(const IBuilder *self) { return flecs::world(static_cast(self)->m_world); } flecs::world world() const { return flecs::world(static_cast(this)->m_world); } }; } // namespace flecs namespace flecs { /** Class that stores a flecs id. * A flecs id is an identifier that can store an entity id, an relation-object * pair, or role annotated id (such as SWITCH | Movement). */ class id : public world_base { public: explicit id(flecs::id_t value = 0) : m_world(nullptr) , m_id(value) { } explicit id(flecs::world_t *world, flecs::id_t value = 0) : m_world(world) , m_id(value) { } explicit id(flecs::world_t *world, flecs::id_t relation, flecs::id_t object) : m_world(world) , m_id(ecs_pair(relation, object)) { } explicit id(flecs::id_t relation, flecs::id_t object) : m_world(nullptr) , m_id(ecs_pair(relation, object)) { } explicit id(const flecs::id& relation, const flecs::id& object) : m_world(relation.world()) , m_id(ecs_pair(relation.m_id, object.m_id)) { } /** Test if id is pair (has relation, object) */ bool is_pair() const { return (m_id & ECS_ROLE_MASK) == flecs::Pair; } /* Test if id is a wildcard */ bool is_wildcard() const { return ecs_id_is_wildcard(m_id); } /* Test if id has the Switch role */ bool is_switch() const { return (m_id & ECS_ROLE_MASK) == flecs::Switch; } /* Test if id has the Case role */ bool is_case() const { return (m_id & ECS_ROLE_MASK) == flecs::Case; } /* Return id as entity (only allowed when id is valid entity) */ flecs::entity entity() const; /* Return id with role added */ flecs::entity add_role(flecs::id_t role) const; /* Return id with role removed */ flecs::entity remove_role(flecs::id_t role) const; /* Return id without role */ flecs::entity remove_role() const; /* Return id without role */ flecs::entity remove_generation() const; /* Test if id has specified role */ bool has_role(flecs::id_t role) const { return ((m_id & ECS_ROLE_MASK) == role); } /* Test if id has any role */ bool has_role() const { return (m_id & ECS_ROLE_MASK) != 0; } flecs::entity role() const; /* Test if id has specified relation */ bool has_relation(flecs::id_t relation) const { if (!is_pair()) { return false; } return ECS_PAIR_RELATION(m_id) == relation; } /** Get relation from pair. * If the id is not a pair, this operation will fail. When the id has a * world, the operation will ensure that the returned id has the correct * generation count. */ flecs::entity relation() const; /** Get object from pair. * If the id is not a pair, this operation will fail. When the id has a * world, the operation will ensure that the returned id has the correct * generation count. */ flecs::entity object() const; /* Convert id to string */ flecs::string str() const { size_t size = ecs_id_str(m_world, m_id, NULL, 0); char *result = static_cast(ecs_os_malloc( static_cast(size) + 1)); ecs_id_str(m_world, m_id, result, size + 1); return flecs::string(result); } /** Convert role of id to string. */ flecs::string role_str() const { return flecs::string_view( ecs_role_str(m_id & ECS_ROLE_MASK)); } ECS_DEPRECATED("use object()") flecs::entity lo() const; ECS_DEPRECATED("use relation()") flecs::entity hi() const; ECS_DEPRECATED("use flecs::id(relation, object)") static flecs::entity comb(entity_view lo, entity_view hi); flecs::id_t raw_id() const { return m_id; } operator flecs::id_t() const { return m_id; } /* World is optional, but guarantees that entity identifiers extracted from * the id are valid */ flecs::world_t *m_world; flecs::id_t m_id; }; } namespace flecs { template class entity_builder_base { public: const Base& base() const { return *static_cast(this); } flecs::world_t* base_world() const { return base().world(); } flecs::entity_t base_id() const { return base().id(); } operator const Base&() const { return this->base(); } }; } #ifdef FLECS_DEPRECATED namespace flecs { struct entity_builder_deprecated_tag { }; /** Deprecated functions */ template class entity_builder_deprecated : public entity_builder_base { public: template ECS_DEPRECATED("use add") const Base& add_trait() const { ecs_add_pair(this->base_world(), this->base_id(), _::cpp_type::id(this->base_world()), _::cpp_type::id(this->base_world())); return *this; } template ECS_DEPRECATED("use add(const entity&)") const Base& add_trait(const Base& c) const { ecs_add_pair(this->base_world(), this->base_id(), _::cpp_type::id(this->base_world()), c.id()); return *this; } template ECS_DEPRECATED("use add_object(const entity&)") const Base& add_trait_tag(const Base& t) const { ecs_add_pair(this->base_world(), this->base_id(), t.id(), _::cpp_type::id(this->base_world())); return *this; } ECS_DEPRECATED("use add(const entity&, const entity&)") const Base& add_trait(const Base& t, const Base& c) const { ecs_add_pair(this->base_world(), this->base_id(), t.id(), c.id()); return *this; } template ECS_DEPRECATED("use remove") const Base& remove_trait() const { ecs_remove_pair(this->base_world(), this->base_id(), _::cpp_type::id(this->base_world()), _::cpp_type::id(this->base_world())); return *this; } template ECS_DEPRECATED("use remove(const entity&)") const Base& remove_trait(const Base& c) const { ecs_remove_pair(this->base_world(), this->base_id(), _::cpp_type::id(this->base_world()), c.id()); return *this; } template ECS_DEPRECATED("use remove_object(const entity&)") const Base& remove_trait_tag(const Base& t) const { ecs_remove_pair(this->base_world(), this->base_id(), t.id(), _::cpp_type::id(this->base_world())); return *this; } ECS_DEPRECATED("use remove(const entity&, const entity&)") const Base& remove_trait(const Base& t, const Base& c) const { ecs_remove_pair(this->base_world(), this->base_id(), t.id(), c.id()); return *this; } template ECS_DEPRECATED("use set(const Relation&)") const Base& set_trait(const T& value) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_set_ptr_w_entity(this->base_world(), this->base_id(), ecs_pair(comp_id, _::cpp_type::id(this->base_world())), sizeof(T), &value); return *this; } template ECS_DEPRECATED("use set(const entity&, const Relation&)") const Base& set_trait(const T& value, const Base& c) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_set_ptr_w_entity(this->base_world(), this->base_id(), ecs_pair(comp_id, c.id()), sizeof(T), &value); return *this; } template ECS_DEPRECATED("use set_object(const entity&, const Object&)") const Base& set_trait_tag(const Base& t, const C& value) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_set_ptr_w_entity(this->base_world(), this->base_id(), ecs_pair(t.id(), comp_id), sizeof(C), &value); return *this; } ECS_DEPRECATED("use set(Func func)") template const Base& patch(const Func& func) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); bool is_added; T *ptr = static_cast(ecs_get_mut_w_entity( this->base_world(), this->base_id(), comp_id, &is_added)); ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); func(*ptr); ecs_modified_w_entity(this->base_world(), this->base_id(), comp_id); return *this; } }; struct entity_deprecated_tag { }; template class entity_deprecated : entity_builder_base { public: template ECS_DEPRECATED("use get") const T* get_trait() const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_id(this->base_world(), this->base_id(), ecs_trait( _::cpp_type::id(this->base_world()), comp_id))); } template ECS_DEPRECATED("use get(const entity&)") const T* get_trait(const Base& c) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_id(this->base_world(), this->base_id(), ecs_trait( c.id(), comp_id))); } template ECS_DEPRECATED("use get_object(const entity&)") const C* get_trait_tag(const Base& t) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_id(this->base_world(), this->base_id(), ecs_trait( comp_id, t.id()))); } ECS_DEPRECATED("use get(const entity&, const entity&)") const void* get_trait(const Base& t, const Base& c) const{ return ecs_get_id(this->base_world(), this->base_id(), ecs_trait(c.id(), t.id())); } template ECS_DEPRECATED("use get_mut(bool)") T* get_trait_mut(bool *is_added = nullptr) const { auto t_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity( this->base_world(), this->base_id(), ecs_trait(_::cpp_type::id(this->base_world()), t_id), is_added)); } template ECS_DEPRECATED("use get_mut(const entity&, bool)") T* get_trait_mut(const Base& c, bool *is_added = nullptr) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity( this->base_world(), this->base_id(), ecs_trait( comp_id, c.id()), is_added)); } template ECS_DEPRECATED("use get_mut_object(const entity&, bool)") C* get_trait_tag_mut(const Base& t, bool *is_added = nullptr) const { auto comp_id = _::cpp_type::id(this->base_world()); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity( this->base_world(), this->base_id(), ecs_trait(comp_id, t.id()), is_added)); } template ECS_DEPRECATED("use has") bool has_trait() const { return ecs_has_entity(this->base_world(), this->base_id(), ecs_trait( _::cpp_type::id(this->base_world()), _::cpp_type::id(this->base_world()))); } template ECS_DEPRECATED("use has(const flecs::entity&)") bool has_trait(const Base& component) const { return ecs_has_entity(this->base_world(), this->base_id(), ecs_trait( component.id(), _::cpp_type::id(this->base_world()))); } template ECS_DEPRECATED("use has_object(const flecs::entity&)") bool has_trait_tag(const Base& trait) const { return ecs_has_entity(this->base_world(), this->base_id(), ecs_trait( _::cpp_type::id(this->base_world()), trait.id())); } ECS_DEPRECATED("use has(const flecs::entity&, const flecs::entity&)") bool has_trait(const Base& trait, const Base& e) const { return ecs_has_entity(this->base_world(), this->base_id(), ecs_trait( e.id(), trait.id())); } }; } #else namespace flecs { template class entity_builder_deprecated { }; class entity_deprecated { }; } #endif namespace flecs { /** Entity view class * This class provides readonly access to entities. Using this class to store * entities in components ensures valid handles, as this class will always store * the actual world vs. a stage. The constructors of this class will never * create a new entity. * * To obtain a mutable handle to the entity, use the "mut" function. */ class entity_view : public id { public: entity_view() : flecs::id() { } /** Wrap an existing entity id. * * @param world The world in which the entity is created. * @param id The entity id. */ explicit entity_view(const flecs::world& world, const entity_view& id) : flecs::id( world.get_world(), id.id() ) { } /** Wrap an existing entity id. * * @param world Pointer to the world in which the entity is created. * @param id The entity id. */ explicit entity_view(world_t *world, const entity_view& id) : flecs::id( flecs::world(world).get_world(), id.id() ) { } /** Implicit conversion from flecs::entity_t to flecs::entity_view. */ entity_view(entity_t id) : flecs::id( nullptr, id ) { } /** Get entity id. * @return The integer entity id. */ entity_t id() const { return m_id; } /** Check is entity is valid. * * @return True if the entity is alive, false otherwise. */ bool is_valid() const { return m_world && ecs_is_valid(m_world, m_id); } explicit operator bool() const { return is_valid(); } /** Check is entity is alive. * * @return True if the entity is alive, false otherwise. */ bool is_alive() const { return m_world && 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. */ flecs::string_view name() const { return flecs::string_view(ecs_get_name(m_world, m_id)); } /** Return the entity path. * * @return The hierarchical entity path, or an empty string if the entity * has no name. */ flecs::string path(const char *sep = "::", const char *init_sep = "::") const { char *path = ecs_get_path_w_sep(m_world, 0, m_id, sep, init_sep); return flecs::string(path); } bool enabled() { return !ecs_has_entity(m_world, m_id, flecs::Disabled); } /** 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; /** Iterate (component) ids of an entity. * The function parameter must match the following signature: * void(*)(flecs::id id) * * @param func The function invoked for each id. */ template void each(const Func& func) const; /** Iterate objects for a given relationship. * This operation will return the object for all ids that match with the * (rel, *) pattern. * * The function parameter must match the following signature: * void(*)(flecs::entity object) * * @param rel The relationship for which to iterate the objects. * @param func The function invoked for each object. */ template void each(const flecs::entity_view& rel, const Func& func) const; /** Iterate objects for a given relationship. * This operation will return the object for all ids that match with the * (Rel, *) pattern. * * The function parameter must match the following signature: * void(*)(flecs::entity object) * * @tparam Rel The relationship for which to iterate the objects. * @param func The function invoked for each object. */ template void each(const Func& func) const { return each(_::cpp_type::id(m_world), func); } /** Find all (component) ids contained by an entity matching a pattern. * This operation will return all ids that match the provided pattern. The * pattern may contain wildcards by using the flecs::Wildcard constant: * * match(flecs::Wildcard, ...) * Matches with all non-pair ids. * * match(world.pair(rel, flecs::Wildcard)) * Matches all pair ids with relationship rel * * match(world.pair(flecs::Wildcard, obj)) * Matches all pair ids with object obj * * The function parameter must match the following signature: * void(*)(flecs::id id) * * @param pattern The pattern to use for matching. * @param func The function invoked for each matching id. */ template void match(flecs::id_t pattern, const Func& func) 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 ::value > = 0> const T* get() const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_id(m_world, m_id, comp_id)); } /** Get component value. * Overload for when T is not the same as the actual type, which happens * when using pair types. * * @tparam T The component to get. * @return Pointer to the component value, nullptr if the entity does not * have the component. */ template , if_not_t< is_actual::value > = 0> const A* get() const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast(ecs_get_id(m_world, m_id, comp_id)); } /** Get a pair. * This operation gets the value for a pair from the entity. * * @tparam R the relation type. * @tparam O the object type. */ template , typename A = actual_type_t

, if_not_t< flecs::is_pair::value > = 0> const A* get() const { return this->get

(); } /** Get a pair. * This operation gets the value for a pair from the entity. * * @tparam R the relation type. * @param object the object. */ template const R* get(const flecs::entity_view& object) const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_id(m_world, m_id, ecs_pair(comp_id, object.id()))); } /** 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(const flecs::entity_view& component) const { return ecs_get_id(m_world, m_id, component.id()); } /** Get a pair (untyped). * This operation gets the value for a pair from the entity. If neither the * relation nor the object part of the pair are components, the operation * will fail. * * @param relation the relation. * @param object the object. */ const void* get(const flecs::entity_view& relation, const flecs::entity_view& object) const { return ecs_get_id(m_world, m_id, ecs_pair(relation.id(), object.id())); } /** Get 1..N components. * This operation accepts a callback with as arguments the components to * retrieve. The callback will only be invoked when the entity has all * the components. * * This operation is faster than individually calling get for each component * as it only obtains entity metadata once. * * @param func The callback to invoke. * @return True if the entity has all components, false if not. */ template ::value > = 0> bool get(const Func& func) const; /** Get the object part from a pair. * This operation gets the value for a pair from the entity. The relation * part of the pair should not be a component. * * @tparam O the object type. * @param relation the relation. */ template const O* get_w_object(const flecs::entity_view& relation) const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_id(m_world, m_id, ecs_pair(relation.id(), comp_id))); } /** Get the object part from a pair. * This operation gets the value for a pair from the entity. The relation * part of the pair should not be a component. * * @tparam R the relation type. * @tparam O the object type. */ template const O* get_w_object() const { return get>(); } /** Get object for a given relation. * This operation returns the object for a given relation. The optional * index can be used to iterate through objects, in case the entity has * multiple instances for the same relation. * * @param relation The relation for which to retrieve the object. * @param index The index (0 for the first instance of the relation). */ flecs::entity get_object(flecs::entity_t relation, int32_t index = 0) const; /** 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(); flecs::entity get_parent(flecs::entity_view e); /** 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. */ flecs::entity lookup(const char *path) const; /** 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 { 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(flecs::id_t e) const { return ecs_has_id(m_world, m_id, e); } /** 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 ecs_has_id(m_world, m_id, _::cpp_type::id(m_world)); } /** Check if entity has the provided pair. * * @tparam Relation The relation type. * @param Object The object type. * @return True if the entity has the provided component, false otherwise. */ template bool has() const { return this->has(_::cpp_type::id(m_world)); } /** Check if entity has the provided pair. * * @tparam Relation The relation type. * @param object The object. * @return True if the entity has the provided component, false otherwise. */ template bool has(flecs::id_t object) const { auto comp_id = _::cpp_type::id(m_world); return ecs_has_id(m_world, m_id, ecs_pair(comp_id, object)); } /** Check if entity has the provided pair. * * @param relation The relation. * @param object The object. * @return True if the entity has the provided component, false otherwise. */ bool has(flecs::id_t relation, flecs::id_t object) const { return ecs_has_id(m_world, m_id, ecs_pair(relation, object)); } /** Check if entity has the provided pair. * * @tparam Object The object type. * @param relation The relation. * @return True if the entity has the provided component, false otherwise. */ template bool has_w_object(flecs::id_t relation) const { auto comp_id = _::cpp_type::id(m_world); return ecs_has_id(m_world, m_id, ecs_pair(relation, comp_id)); } /** 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 { 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(flecs::id_t e) const { return ecs_owns_entity(m_world, m_id, e, true); } /** Check if entity owns the provided pair. * * @tparam Relation The relation type. * @param object The object. * @return True if the entity owns the provided component, false otherwise. */ template bool owns(flecs::id_t object) const { auto comp_id = _::cpp_type::id(m_world); return owns(ecs_pair(comp_id, object)); } /** Check if entity owns the provided pair. * * @param relation The relation. * @param object The object. * @return True if the entity owns the provided component, false otherwise. */ bool owns(flecs::id_t relation, flecs::id_t object) const { return owns(ecs_pair(relation, object)); } /** 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(_::cpp_type::id(m_world)); } /** 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(const flecs::type& sw) const; template bool has_switch() const { return ecs_has_entity(m_world, m_id, flecs::Switch | _::cpp_type::id(m_world)); } /** 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::id_t sw_case) const { return ecs_has_entity(m_world, m_id, flecs::Case | sw_case); } template bool has_case() const { return this->has_case(_::cpp_type::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::id_t sw) const; /** 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. */ template flecs::entity get_case() const; /** 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(const 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, _::cpp_type::id(m_world)); } /** 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_view& e) { return ecs_is_component_enabled_w_entity( m_world, m_id, 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; /** Return mutable entity handle for current stage * When an entity handle created from the world is used while the world is * in staged mode, it will only allow for readonly operations since * structural changes are not allowed on the world while in staged mode. * * To do mutations on the entity, this operation provides a handle to the * entity that uses the stage instead of the actual world. * * Note that staged entity handles should never be stored persistently, in * components or elsewhere. An entity handle should always point to the * main world. * * Also note that this operation is not necessary when doing mutations on an * entity outside of a system. It is allowed to do entity operations * directly on the world, as long as the world is not in staged mode. * * @param stage The current stage. * @return An entity handle that allows for mutations in the current stage. */ flecs::entity mut(const flecs::world& stage) const; /** Same as mut(world), but for iterator. * This operation allows for the construction of a mutable entity handle * from an iterator. * * @param stage An created for the current stage. * @return An entity handle that allows for mutations in the current stage. */ flecs::entity mut(const flecs::iter& it) const; /** Same as mut(world), but for entity. * This operation allows for the construction of a mutable entity handle * from another entity. This is useful in each() functions, which only * provide a handle to the entity being iterated over. * * @param stage An created for the current stage. * @return An entity handle that allows for mutations in the current stage. */ flecs::entity mut(const flecs::entity_view& e) const; private: flecs::entity set_stage(world_t *stage); }; /** 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(); */ struct entity_builder_tag { }; // Tag to prevent ambiguous base template class entity_builder : public entity_builder_base { public: /** 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 const Base& add() const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); ecs_add_id(this->base_world(), this->base_id(), _::cpp_type::id(this->base_world())); return *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. */ const Base& add(entity_t entity) const { ecs_add_id(this->base_world(), this->base_id(), entity); return *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. */ const Base& add(const type& type) const; /** Add a pair. * This operation adds a pair to the entity. * * @param relation The relation id. * @param object The object id. */ const Base& add(entity_t relation, entity_t object) const { ecs_add_pair(this->base_world(), this->base_id(), relation, object); return *this; } /** Add a pair. * This operation adds a pair to the entity. * * @tparam R the relation type. * @tparam O the object type. */ template const Base& add() const { return this->add(_::cpp_type::id(this->base_world())); } /** Add a pair. * This operation adds a pair to the entity. * * @tparam R the relation type. * @param object the object type. */ template const Base& add(entity_t object) const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); return this->add(_::cpp_type::id(this->base_world()), object); } /** Shortcut for add(IsA. obj). * * @param object the object id. */ const Base& is_a(entity_t object) const { return this->add(flecs::IsA, object); } template const Base& is_a() const { return this->add(flecs::IsA, _::cpp_type::id(this->base_world())); } /** Shortcut for add(ChildOf. obj). * * @param object the object id. */ const Base& child_of(entity_t object) const { return this->add(flecs::ChildOf, object); } /** Shortcut for add(ChildOf. obj). * * @param object the object id. */ template const Base& child_of() const { return this->add(flecs::ChildOf, _::cpp_type::id(this->base_world())); } /** Add a pair with object type. * This operation adds a pair to the entity. The relation part of the pair * should not be a component. * * @param relation the relation type. * @tparam O the object type. */ template const Base& add_w_object(entity_t relation) const { flecs_static_assert(is_flecs_constructible::value, "cannot default construct type: add T::T() or use emplace()"); return this->add(relation, _::cpp_type::id(this->base_world())); } /** Remove a component from an entity. * * @tparam T the type of the component to remove. */ template const Base& remove() const { ecs_remove_id(this->base_world(), this->base_id(), _::cpp_type::id(this->base_world())); return *this; } /** Remove an entity from an entity. * * @param entity The entity to remove. */ const Base& remove(entity_t entity) const { ecs_remove_id(this->base_world(), this->base_id(), entity); return *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. */ const Base& remove(const type& type) const; /** Remove a pair. * This operation removes a pair from the entity. * * @param relation The relation id. * @param object The object id. */ const Base& remove(entity_t relation, entity_t object) const { ecs_remove_pair(this->base_world(), this->base_id(), relation, object); return *this; } /** Removes a pair. * This operation removes a pair from the entity. * * @tparam Relation the relation type. * @tparam Object the object type. */ template const Base& remove() const { return this->remove(_::cpp_type::id(this->base_world())); } /** Remove a pair. * This operation adds a pair to the entity. * * @tparam Relation the relation type. * @param object the object type. */ template const Base& remove(entity_t object) const { return this->remove(_::cpp_type::id(this->base_world()), object); } /** Removes a pair with object type. * This operation removes a pair from the entity. * * @param relation the relation type. * @tparam Object the object type. */ template const Base& remove_w_object(entity_t relation) const { return this->remove(relation, _::cpp_type::id(this->base_world())); } /** Add owned flag for component (forces ownership when instantiating) * * @param entity The entity for which to add the OWNED flag */ const Base& add_owned(entity_t entity) const { ecs_add_id(this->base_world(), this->base_id(), ECS_OWNED | entity); return *this; } /** Add owned flag for component (forces ownership when instantiating) * * @tparam T The component for which to add the OWNED flag */ template const Base& add_owned() const { ecs_add_id(this->base_world(), this->base_id(), ECS_OWNED | _::cpp_type::id(this->base_world())); return *this; } ECS_DEPRECATED("use add_owned(flecs::entity e)") const Base& add_owned(const type& type) const; /** Set value, add owned flag. * * @tparam T The component to set and for which to add the OWNED flag */ template const Base& set_owned(T&& val) const { this->add_owned(); this->set(std::forward(val)); return *this; } /** 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. */ const Base& add_switch(entity_t sw) const { ecs_add_id(this->base_world(), this->base_id(), ECS_SWITCH | sw); return *this; } /** Add a switch to an entity by C++ type. * The C++ type must be associated with a switch type. * * @param sw The switch to add. */ template const Base& add_switch() const { ecs_add_id(this->base_world(), this->base_id(), ECS_SWITCH | _::cpp_type::id()); return *this; } /** Add a switch to an entity. * Any instance of flecs::type can be used as a switch. * * @param sw The switch to add. */ const Base& add_switch(const type& sw) const; /** Remove a switch from an entity by id. * * @param sw The switch entity id to remove. */ const Base& remove_switch(entity_t sw) const { ecs_remove_id(this->base_world(), this->base_id(), ECS_SWITCH | sw); return *this; } /** Add a switch to an entity by C++ type. * The C++ type must be associated with a switch type. * * @param sw The switch to add. */ template const Base& remove_switch() const { ecs_remove_id(this->base_world(), this->base_id(), ECS_SWITCH | _::cpp_type::id()); return *this; } /** Remove a switch from an entity. * Any instance of flecs::type can be used as a switch. * * @param sw The switch to remove. */ const Base& 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. */ const Base& add_case(entity_t sw_case) const { ecs_add_id(this->base_world(), this->base_id(), ECS_CASE | sw_case); return *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 const Base& add_case() const { return this->add_case(_::cpp_type::id()); } /** 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. */ const Base& remove_case(entity_t sw_case) const { ecs_remove_id(this->base_world(), this->base_id(), ECS_CASE | sw_case); return *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 const Base& remove_case() const { return this->remove_case(_::cpp_type::id()); } /** Enable an entity. * Enabled entities are matched with systems and can be searched with * queries. */ const Base& enable() const { ecs_enable(this->base_world(), this->base_id(), true); return *this; } /** Disable an entity. * Disabled entities are not matched with systems and cannot be searched * with queries, unless explicitly specified in the query expression. */ const Base& disable() const { ecs_enable(this->base_world(), this->base_id(), false); return *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 const Base& enable() const { ecs_enable_component_w_entity(this->base_world(), this->base_id(), _::cpp_type::id(), true); return *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 const Base& disable() const { ecs_enable_component_w_entity(this->base_world(), this->base_id(), _::cpp_type::id(), false); return *this; } /** Enable a component. * See enable. * * @param component The component to enable. */ const Base& enable(entity_t comp) const { ecs_enable_component_w_entity(this->base_world(), this->base_id(), comp, true); return *this; } /** Disable a component. * See disable. * * @param component The component to disable. */ const Base& disable(entity_t comp) const { ecs_enable_component_w_entity(this->base_world(), this->base_id(), comp, false); return *this; } template::value && is_actual::value> = 0 > const Base& set(T&& value) const { flecs::set(this->base_world(), this->base_id(), std::forward(value)); return *this; } template::value && is_actual::value > = 0> const Base& set(const T& value) const { flecs::set(this->base_world(), this->base_id(), value); return *this; } template, if_not_t< is_callable::value || is_actual::value > = 0> const Base& set(A&& value) const { flecs::set(this->base_world(), this->base_id(), std::forward(value)); return *this; } template, if_not_t< is_callable::value || is_actual::value > = 0> const Base& set(const A& value) const { flecs::set(this->base_world(), this->base_id(), value); return *this; } /** Set a pair for an entity. * This operation sets the pair value, and uses the relation as type. If the * entity did not yet have the pair, it will be added. * * @tparam R The relation part of the pair. * @tparam O The object part of the pair. * @param value The value to set. */ template , typename A = actual_type_t

, if_not_t< is_pair::value> = 0> const Base& set(const A& value) const { flecs::set

(this->base_world(), this->base_id(), value); return *this; } /** Set a pair for an entity. * This operation sets the pair value, and uses the relation as type. If the * entity did not yet have the pair, it will be added. * * @tparam R The relation part of the pair. * @param object The object part of the pair. * @param value The value to set. */ template const Base& set(entity_t object, const R& value) const { auto relation = _::cpp_type::id(this->base_world()); flecs::set(this->base_world(), this->base_id(), value, ecs_pair(relation, object)); return *this; } /** Set a pair for an entity. * This operation sets the pair value, and uses the relation as type. If the * entity did not yet have the pair, it will be added. * * @tparam Object The object part of the pair. * @param relation The relation part of the pair. * @param value The value to set. */ template const Base& set_w_object(entity_t relation, const O& value) const { auto object = _::cpp_type::id(this->base_world()); flecs::set(this->base_world(), this->base_id(), value, ecs_pair(relation, object)); return *this; } template const Base& set_w_object(const O& value) const { flecs::set>(this->base_world(), this->base_id(), value); return *this; } /** Set 1..N components. * This operation accepts a callback with as arguments the components to * set. If the entity does not have all of the provided components, they * will be added. * * This operation is faster than individually calling get for each component * as it only obtains entity metadata once. When this operation is called * while deferred, its performance is equivalent to that of calling get_mut * for each component separately. * * The operation will invoke modified for each component after the callback * has been invoked. * * @param func The callback to invoke. */ template ::value > = 0> const Base& set(const Func& func) const; /** Emplace component. * Emplace constructs a component in the storage, which prevents calling the * destructor on the object passed into the function. * * Emplace attempts the following signatures to construct the component: * T{Args...} * T{flecs::entity, Args...} * * If the second signature matches, emplace will pass in the current entity * as argument to the constructor, which is useful if the component needs * to be aware of the entity to which it has been added. * * Emplace may only be called for components that have not yet been added * to the entity. * * @tparam T the component to emplace * @param args The arguments to pass to the constructor of T */ template const Base& emplace(Args&&... args) const { flecs::emplace(this->base_world(), this->base_id(), std::forward(args)...); return *this; } /** Entities created in function will have the current entity. * * @param func The function to call. */ template const Base& with(const Func& func) const { ecs_id_t prev = ecs_set_with(this->base_world(), this->base_id()); func(); ecs_set_with(this->base_world(), prev); return *this; } /** Entities created in function will have (Relation, this) * This operation is thread safe. * * @tparam Relation The relation to use. * @param func The function to call. */ template const Base& with(const Func& func) const { with(_::cpp_type::id(this->base_world()), func); return *this; } /** Entities created in function will have (relation, this) * * @param relation The relation to use. * @param func The function to call. */ template const Base& with(id_t relation, const Func& func) const { ecs_id_t prev = ecs_set_with(this->base_world(), ecs_pair(relation, this->base_id())); func(); ecs_set_with(this->base_world(), prev); return *this; } /** The function will be ran with the scope set to the current entity. */ template const Base& scope(const Func& func) const { ecs_entity_t prev = ecs_set_scope(this->base_world(), this->base_id()); func(); ecs_set_scope(this->base_world(), prev); return *this; } /** Associate entity with type. * This operation enables using a type to refer to an entity, as it * associates the entity id with the provided type. * * If the entity does not have a name, a name will be derived from the type. * If the entity already is a component, the provided type must match in * size with the component size of the entity. After this operation the * entity will be a component (it will have the EcsComponent component) if * the type has a non-zero size. * * @tparam T the type to associate with the entity. */ template const Base& component() const; /* Set the entity name. */ const Base& set_name(const char *name) const { ecs_set_name(this->base_world(), this->base_id(), name); return *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() { auto comp_id = _::cpp_type::id(world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); ecs_get_ref_w_id( m_world, &m_ref, m_entity, comp_id); } const T* operator->() { const T* result = static_cast(ecs_get_ref_w_id( m_world, &m_ref, m_entity, _::cpp_type::id(m_world))); ecs_assert(result != NULL, ECS_INVALID_PARAMETER, NULL); return result; } const T* get() { if (m_entity) { ecs_get_ref_w_id( m_world, &m_ref, m_entity, _::cpp_type::id(m_world)); } return static_cast(m_ref.ptr); } flecs::entity entity() const; private: world_t *m_world; entity_t m_entity; flecs::ref_t m_ref; }; /** Entity class * This class provides access to entities. */ class entity : public entity_view, public entity_builder, public entity_deprecated, public entity_builder_deprecated { public: /** Default constructor. */ entity() : flecs::entity_view() { } /** Create entity. * * @param world The world in which to create the entity. */ explicit entity(world_t *world) : flecs::entity_view() { m_world = world; m_id = ecs_new_w_type(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). */ explicit entity(world_t *world, const char *name) : flecs::entity_view() { m_world = world; ecs_entity_desc_t desc = {}; desc.name = name; desc.sep = "::"; m_id = ecs_entity_init(world, &desc); } /** Wrap an existing entity id. * * @param world The world in which the entity is created. * @param id The entity id. */ explicit entity(world_t *world, entity_t id) : flecs::entity_view() { m_world = world; m_id = id; } /** Conversion from flecs::entity_t to flecs::entity. */ explicit entity(entity_t id) : flecs::entity_view( nullptr, id ) { } /** Get entity id. * @return The integer entity id. */ entity_t id() const { return m_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 { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity(m_world, m_id, comp_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 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 comp, bool *is_added = nullptr) const { return ecs_get_mut_w_entity(m_world, m_id, comp, is_added); } /** Get mutable pointer for a pair. * This operation gets the value for a pair from the entity. * * @tparam Relation the relation type. * @tparam Object the object type. */ template Relation* get_mut(bool *is_added = nullptr) const { return this->get_mut( _::cpp_type::id(m_world), is_added); } /** Get mutable pointer for a pair. * This operation gets the value for a pair from the entity. * * @tparam Relation the relation type. * @param object the object. */ template Relation* get_mut(entity_t object, bool *is_added = nullptr) const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity(m_world, m_id, ecs_pair(comp_id, object), is_added)); } /** Get mutable pointer for a pair (untyped). * This operation gets the value for a pair from the entity. If neither the * relation or object are a component, the operation will fail. * * @param relation the relation. * @param object the object. */ void* get_mut(entity_t relation, entity_t object, bool *is_added = nullptr) const { return ecs_get_mut_w_entity(m_world, m_id, ecs_pair(relation, object), is_added); } /** Get mutable pointer for the object from a pair. * This operation gets the value for a pair from the entity. * * @tparam Object the object type. * @param relation the relation. */ template Object* get_mut_w_object(entity_t relation, bool *is_added = nullptr) const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return static_cast( ecs_get_mut_w_entity(m_world, m_id, ecs_pair(relation, comp_id), is_added)); } /** Signal that component was modified. * * @tparam T component that was modified. */ template void modified() const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); this->modified(comp_id); } /** Signal that the relation part of a pair was modified. * * @tparam Relation the relation type. * @tparam Object the object type. */ template void modified() const { this->modified(_::cpp_type::id(m_world)); } /** Signal that the relation part of a pair was modified. * * @tparam Relation the relation type. * @param object the object. */ template void modified(entity_t object) const { auto comp_id = _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); this->modified(comp_id, object); } /** Signal that a pair has modified (untyped). * If neither the relation or object part of the pair are a component, the * operation will fail. * * @param relation the relation. * @param object the object. */ void modified(entity_t relation, entity_t object) const { this->modified(ecs_pair(relation, object)); } /** Signal that component was modified. * * @param component component that was modified. */ void modified(entity_t comp) const { ecs_modified_w_entity(m_world, m_id, comp); } /** 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 { // Ensure component is registered _::cpp_type::id(m_world); ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); return ref(m_world, m_id); } /** Clear an entity. * This operation removes all components from an entity without recycling * the entity id. */ void clear() const { 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_delete(m_world, m_id); } /** Used by builder class. Do not invoke (deprecated). */ template void invoke(Func&& action) const { action(m_world, m_id); } /** 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 flecs::world& world) { return flecs::entity(world.get_world().c_ptr(), static_cast(0)); } static flecs::entity null() { return flecs::entity(static_cast(0)); } }; /** Prefab class */ class prefab final : public entity { public: explicit prefab(world_t *world, const char *name = nullptr) : entity(world, name) { this->add(flecs::Prefab); } }; } // namespace flecs //////////////////////////////////////////////////////////////////////////////// //// Register component, provide global access to component handles / metadata //////////////////////////////////////////////////////////////////////////////// namespace flecs { 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; } }; #else #error "implicit component registration not supported" #endif // Translate a typename into a language-agnostic identifier. This allows for // registration of components/modules across language boundaries. template struct symbol_helper { static char* symbol(void) { const char *name = name_helper::name(); // Symbol is same as name, but with '::' replaced with '.' char *ptr, *sym = ecs_os_strdup(name); ecs_size_t i, len = ecs_os_strlen(sym); ptr = sym; for (i = 0, ptr = sym; i < len && *ptr; i ++, ptr ++) { if (*ptr == ':') { sym[i] = '.'; ptr ++; } else { sym[i] = *ptr; } } sym[i] = '\0'; return sym; } }; // If type is trivial, don't register lifecycle actions. While the functions // that obtain the lifecycle callback do detect whether the callback is required // adding a special case for trivial types eases the burden a bit on the // compiler as it reduces the number of templates to evaluate. template::value == true >* = nullptr> void register_lifecycle_actions(ecs_world_t*, ecs_entity_t) { } // If the component is non-trivial, register component lifecycle actions. // Depending on the type not all callbacks may be available. template::value == false >* = nullptr> void register_lifecycle_actions( ecs_world_t *world, ecs_entity_t component) { if (!ecs_component_has_actions(world, component)) { EcsComponentLifecycle cl{}; cl.ctor = ctor(); cl.dtor = dtor(); cl.copy = copy(); cl.copy_ctor = copy_ctor(); cl.move = move(); cl.move_ctor = move_ctor(); cl.ctor_move_dtor = ctor_move_dtor(); cl.move_dtor = move_dtor(); ecs_set_component_actions_w_entity( world, component, &cl); } } // Class that manages component ids across worlds & binaries. // The cpp_type class stores the component id for a C++ type in a static global // variable that is shared between worlds. Whenever a component is used this // class will check if it already has been registered (has the global id been // set), and if not, register the component with the world. // // If the id has been set, the class will ensure it is known by the world. If it // is not known the component has been registered by another world and will be // registered with the world using the same id. If the id does exist, the class // will register it as a component, and verify whether the input is consistent. template class cpp_type_size { public: static size_t size(bool allow_tag) { // 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 (allow_tag && std::is_empty::value) { return 0; } else { return sizeof(T); } } static size_t alignment(bool allow_tag) { if (size(allow_tag) == 0) { return 0; } else { return alignof(T); } } }; template class cpp_type_impl { 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()); ecs_assert(allow_tag == s_allow_tag, ECS_INTERNAL_ERROR, NULL); // 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 = flecs::string(path); s_allow_tag = allow_tag; } // Names returned from the name_helper class do not start with :: // but are relative to the root. If the namespace of the type // overlaps with the namespace of the current module, strip it from // the implicit identifier. // This allows for registration of component types that are not in the // module namespace to still be registered under the module scope. static const char* strip_module(world_t *world) { const char *name = _::name_helper::name(); entity_t scope = ecs_get_scope(world); if (!scope) { return name; } char *path = ecs_get_path_w_sep(world, 0, scope, "::", nullptr); if (path) { const char *ptr = strrchr(name, ':'); ecs_assert(ptr != name, ECS_INTERNAL_ERROR, NULL); if (ptr) { ptr --; ecs_assert(ptr[0] == ':', ECS_INTERNAL_ERROR, NULL); ecs_size_t name_path_len = static_cast(ptr - name); if (name_path_len <= ecs_os_strlen(path)) { if (!ecs_os_strncmp(name, path, name_path_len)) { name = &name[name_path_len + 2]; } } } } ecs_os_free(path); return name; } // Obtain a component identifier for explicit component registration. static entity_t id_explicit(world_t *world = nullptr, const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0) { if (!s_id) { // If no world was provided the component cannot be registered ecs_assert(world != nullptr, ECS_COMPONENT_NOT_REGISTERED, name); s_allow_tag = allow_tag; } else { ecs_assert(!id || s_id == id, ECS_INCONSISTENT_COMPONENT_ID, NULL); ecs_assert(s_allow_tag == allow_tag, ECS_INVALID_PARAMETER, NULL); } // If no id has been registered yet for the component (indicating the // component has not yet been registered, or the component is used // across more than one binary), or if the id does not exists in the // world (indicating a multi-world application), register it. */ if (!s_id || (world && !ecs_exists(world, s_id))) { if (!s_id) { s_id = id; } // One type can only be associated with a single type ecs_assert(!id || s_id == id, ECS_INTERNAL_ERROR, NULL); char *symbol = nullptr; // If an explicit id is provided, it is possible that the symbol and // name differ from the actual type, as the application may alias // one type to another. if (!id) { symbol = symbol_helper::symbol(); if (!name) { // If no name was provided, retrieve the name implicitly from // the name_helper class. name = strip_module(world); } } else { // If an explicit id is provided but it has no name, inherit // the name from the type. if (!ecs_get_name(world, id)) { name = strip_module(world); } } ecs_component_desc_t desc = {}; desc.entity.entity = s_id; desc.entity.name = name; desc.entity.sep = "::"; desc.entity.root_sep = "::"; desc.entity.symbol = symbol; desc.size = cpp_type_size::size(allow_tag); desc.alignment = cpp_type_size::alignment(allow_tag); ecs_entity_t entity = ecs_component_init(world, &desc); ecs_assert(entity != 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(!s_id || s_id == entity, ECS_INTERNAL_ERROR, NULL); ecs_os_free(symbol); init(world, s_id, allow_tag); s_id = entity; } // By now the identifier must be valid and known with the world. ecs_assert(s_id != 0 && ecs_exists(world, s_id), ECS_INTERNAL_ERROR, NULL); return s_id; } // Obtain a component identifier for implicit component registration. This // is almost the same as id_explicit, except that this operation // automatically registers lifecycle callbacks. // Additionally, implicit registration temporarily resets the scope & with // state of the world, so that the component is not implicitly created with // the scope/with of the code it happens to be first used by. static id_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 || (world && !ecs_exists(world, s_id))) { ecs_entity_t prev_scope = 0; ecs_id_t prev_with = 0; if (world) { prev_scope = ecs_set_scope(world, 0); prev_with = ecs_set_with(world, 0); } // This will register a component id, but will not register // lifecycle callbacks. id_explicit(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); } if (prev_with) { ecs_set_with(world, prev_with); } if (prev_scope) { ecs_set_scope(world, prev_scope); } } // 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_explicit() 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_explicit(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_id(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() { ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); return cpp_type_size::size(s_allow_tag); } // Return the alignment of a component. static size_t alignment() { ecs_assert(s_id != 0, ECS_INTERNAL_ERROR, NULL); return cpp_type_size::alignment(s_allow_tag); } // 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 flecs::string s_name; static flecs::string s_symbol; static bool s_allow_tag; }; // Global templated variables that hold component identifier and other info template entity_t cpp_type_impl::s_id( 0 ); template type_t cpp_type_impl::s_type( nullptr ); template flecs::string cpp_type_impl::s_name; template bool cpp_type_impl::s_allow_tag( true ); // Front facing class for implicitly registering a component & obtaining // static component data // Regular type template class cpp_type::value >> : public cpp_type_impl> { }; // Pair type template class cpp_type::value >> { public: // Override id method to return id of pair static id_t id(world_t *world = nullptr) { return ecs_pair( cpp_type< pair_relation_t >::id(world), cpp_type< pair_object_t >::id(world)); } }; } // namespace _ //////////////////////////////////////////////////////////////////////////////// //// Register a component with flecs //////////////////////////////////////////////////////////////////////////////// /** Plain old datatype, no lifecycle actions are registered */ template flecs::entity pod_component( flecs::world_t *world, const char *name = nullptr, bool allow_tag = true, flecs::id_t id = 0) { const char *n = name; bool implicit_name = false; if (!n) { n = _::name_helper::name(); /* Keep track of whether name was explicitly set. If not, and the * component was already registered, just use the registered name. * * The registered name may differ from the typename as the registered * name includes the flecs scope. This can in theory be different from * the C++ namespace though it is good practice to keep them the same */ implicit_name = true; } if (_::cpp_type::registered()) { /* Obtain component id. Because the component is already registered, * this operation does nothing besides returning the existing id */ id = _::cpp_type::id_explicit(world, name, allow_tag, id); /* If entity is not empty check if the name matches */ if (ecs_get_type(world, id) != nullptr) { if (!implicit_name && id >= EcsFirstUserComponentId) { char *path = ecs_get_path_w_sep( world, 0, id, "::", nullptr); ecs_assert(!strcmp(path, n), ECS_INCONSISTENT_NAME, name); ecs_os_free(path); } } else { /* Register name with entity, so that when the entity is created the * correct id will be resolved from the name. Only do this when the * entity is empty.*/ ecs_add_path_w_sep(world, id, 0, n, "::", "::"); } /* If a component was already registered with this id but with a * different size, the ecs_component_init function will fail. */ /* We need to explicitly call ecs_component_init 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_component_desc_t desc = {}; desc.entity.entity = id; desc.size = _::cpp_type::size(); desc.alignment = _::cpp_type::alignment(); ecs_entity_t entity = ecs_component_init(world, &desc); (void)entity; ecs_assert(entity == id, ECS_INTERNAL_ERROR, NULL); /* This functionality could have been put in id_explicit, but since * this code happens when a component is registered, and the entire API * calls id_explicit, 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. Ensure component is * looked up from root. */ ecs_entity_t prev_scope = ecs_set_scope(world, 0); ecs_entity_t entity; if (id) { entity = id; } else { entity = ecs_lookup_path_w_sep(world, 0, n, "::", "::", false); } ecs_set_scope(world, prev_scope); /* If entity exists, compare symbol name to ensure that the component * we are trying to register under this name is the same */ if (entity) { if (!id) { const char *sym = ecs_get_symbol(world, entity); ecs_assert(sym != NULL, ECS_INTERNAL_ERROR, NULL); (void)sym; char *symbol = _::symbol_helper::symbol(); ecs_assert(!ecs_os_strcmp(sym, symbol), ECS_NAME_IN_USE, n); ecs_os_free(symbol); /* If an existing id was provided, it's possible that this id was * registered with another type. Make sure that in this case at * least the component size/alignment matches. * This allows applications to alias two different types to the same * id, which enables things like redefining a C type in C++ by * inheriting from it & adding utility functions etc. */ } else { const EcsComponent *comp = ecs_get(world, entity, EcsComponent); if (comp) { ecs_assert(comp->size == ECS_SIZEOF(T), ECS_INVALID_COMPONENT_SIZE, NULL); ecs_assert(comp->alignment == ECS_ALIGNOF(T), ECS_INVALID_COMPONENT_ALIGNMENT, NULL); } else { /* If the existing id is not a component, no checking is * needed. */ } } /* If no entity is found, lookup symbol to check if the component was * registered under a different name. */ } else { char *symbol = _::symbol_helper::symbol(); entity = ecs_lookup_symbol(world, symbol, false); ecs_assert(entity == 0, ECS_INCONSISTENT_COMPONENT_ID, symbol); ecs_os_free(symbol); } /* Register id as usual */ id = _::cpp_type::id_explicit(world, name, allow_tag, id); } return flecs::entity(world, id); } /** Register component */ template flecs::entity component(flecs::world_t *world, const char *name = nullptr) { flecs::entity result = pod_component(world, name); if (_::cpp_type::size()) { _::register_lifecycle_actions(world, result); } return result; } /* Register component with existing entity id */ template void component_for_id(flecs::world_t *world, flecs::id_t id) { flecs::entity result = pod_component(world, nullptr, true, id); ecs_assert(result.id() == id, ECS_INTERNAL_ERROR, NULL); if (_::cpp_type::size()) { _::register_lifecycle_actions(world, result); } } ECS_DEPRECATED("API detects automatically whether type is trivial") 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()); return result; } template flecs::entity_t type_id() { return _::cpp_type::id(); } } // namespace flecs //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system each //////////////////////////////////////////////////////////////////////////////// namespace flecs { namespace _ { // Utility to convert template argument pack to array of term ptrs struct term_ptr { void *ptr; bool is_ref; }; template class term_ptrs { public: using array = flecs::array<_::term_ptr, sizeof...(Components)>; bool populate(const ecs_iter_t *iter) { return populate(iter, 0, static_cast< remove_reference_t< remove_pointer_t> *>(nullptr)...); } array m_terms; private: /* Populate terms array without checking for references */ bool populate(const ecs_iter_t*, size_t) { return false; } template bool populate(const ecs_iter_t *iter, size_t index, T, Targs... comps) { m_terms[index].ptr = iter->ptrs[index]; bool is_ref = iter->subjects && iter->subjects[index] != 0; m_terms[index].is_ref = is_ref; is_ref |= populate(iter, index + 1, comps ...); return is_ref; } }; class invoker { }; // Template that figures out from the template parameters of a query/system // how to pass the value to the each callback template struct each_column { }; // Base class struct each_column_base { each_column_base(const _::term_ptr& term, size_t row) : m_term(term), m_row(row) { } protected: const _::term_ptr& m_term; size_t m_row; }; // If type is not a pointer, return a reference to the type (default case) template struct each_column::value && is_actual::value > > : public each_column_base { each_column(const _::term_ptr& term, size_t row) : each_column_base(term, row) { } T& get_row() { return static_cast(this->m_term.ptr)[this->m_row]; } }; // If argument type is not the same as actual component type, return by value. // This requires that the actual type can be converted to the type. // A typical scenario where this happens is when using flecs::pair types. template struct each_column::value && !is_actual::value> > : public each_column_base { each_column(const _::term_ptr& term, size_t row) : each_column_base(term, row) { } T get_row() { return static_cast*>(this->m_term.ptr)[this->m_row]; } }; // If type is a pointer (indicating an optional value) return the type as is template struct each_column::value > > : public each_column_base { each_column(const _::term_ptr& term, size_t row) : each_column_base(term, row) { } T get_row() { if (this->m_term.ptr) { return &static_cast>(this->m_term.ptr)[this->m_row]; } else { // optional argument doesn't hava a value return nullptr; } } }; // If the query contains component references to other entities, check if the // current argument is one. template struct each_ref_column : public each_column { each_ref_column(const _::term_ptr& term, size_t row) : each_column(term, row) { if (term.is_ref) { // If this is a reference, set the row to 0 as a ref always is a // single value, not an array. This prevents the application from // having to do an if-check on whether the column is owned. // // This check only happens when the current table being iterated // over caused the query to match a reference. The check is // performed once per iterated table. this->m_row = 0; } } }; template class each_invoker : public invoker { public: // If the number of arguments in the function signature is one more than the // number of components in the query, an extra entity arg is required. static constexpr bool PassEntity = sizeof...(Components) == (arity::value - 1); static_assert(arity::value > 0, "each() must have at least one argument"); using Terms = typename term_ptrs::array; explicit each_invoker(Func&& func) noexcept : m_func(std::move(func)) { } explicit each_invoker(const Func& func) noexcept : m_func(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the invoker, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { term_ptrs terms; if (terms.populate(iter)) { invoke_callback< each_ref_column >(iter, m_func, 0, terms.m_terms); } else { invoke_callback< each_column >(iter, m_func, 0, terms.m_terms); } } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { auto self = static_cast(iter->binding_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } private: // Number of function arguments is one more than number of components, pass // entity as argument. template class ColumnType, typename... Args, if_t< sizeof...(Components) == sizeof...(Args) && PassEntity> = 0> static void invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { #ifndef NDEBUG ecs_table_t *table = iter->table; if (table) { ecs_table_lock(iter->world, table); } #endif flecs::iter it(iter); for (auto row : it) { func(it.entity(row), (ColumnType< remove_reference_t >(comps, row) .get_row())...); } #ifndef NDEBUG if (table) { ecs_table_unlock(iter->world, table); } #endif } // Number of function arguments is equal to number of components, no entity template class ColumnType, typename... Args, if_t< sizeof...(Components) == sizeof...(Args) && !PassEntity> = 0> static void invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Args... comps) { flecs::iter it(iter); for (auto row : it) { func( (ColumnType< remove_reference_t >(comps, row) .get_row())...); } } template class ColumnType, typename... Args, if_t< sizeof...(Components) != sizeof...(Args) > = 0> static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t index, Terms& columns, Args... comps) { invoke_callback( iter, func, index + 1, columns, comps..., columns[index]); } Func m_func; }; //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system iterate action //////////////////////////////////////////////////////////////////////////////// template class iter_invoker : public invoker { static constexpr bool IterOnly = arity::value == 1; using Terms = typename term_ptrs::array; public: explicit iter_invoker(Func&& func) noexcept : m_func(std::move(func)) { } explicit iter_invoker(const Func& func) noexcept : m_func(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the invoker, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { term_ptrs terms; terms.populate(iter); invoke_callback(iter, m_func, 0, terms.m_terms); } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { auto self = static_cast(iter->binding_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } private: template = 0> static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t, Terms&, Args...) { flecs::iter it(iter); func(it); } template = 0> static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t, Terms&, Targs... comps) { flecs::iter it(iter); #ifndef NDEBUG ecs_table_t *table = iter->table; if (table) { ecs_table_lock(iter->world, table); } #endif func(it, ( static_cast< remove_reference_t< remove_pointer_t< actual_type_t > >* > (comps.ptr))...); #ifndef NDEBUG if (table) { ecs_table_unlock(iter->world, table); } #endif } template = 0> static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t index, Terms& columns, Targs... comps) { invoke_callback(iter, func, index + 1, columns, comps..., columns[index]); } Func m_func; }; //////////////////////////////////////////////////////////////////////////////// //// Utility class to invoke a system action (deprecated) //////////////////////////////////////////////////////////////////////////////// template class action_invoker : public invoker { using Terms = typename term_ptrs::array; public: explicit action_invoker(Func&& func) noexcept : m_func(std::move(func)) { } explicit action_invoker(const Func& func) noexcept : m_func(func) { } // Invoke object directly. This operation is useful when the calling // function has just constructed the invoker, such as what happens when // iterating a query. void invoke(ecs_iter_t *iter) const { term_ptrs terms; terms.populate(iter); invoke_callback(iter, m_func, 0, terms.m_terms); } // Static function that can be used as callback for systems/triggers static void run(ecs_iter_t *iter) { auto self = static_cast(iter->binding_ctx); ecs_assert(self != nullptr, ECS_INTERNAL_ERROR, NULL); self->invoke(iter); } private: template = 0> static void invoke_callback( ecs_iter_t *iter, const Func& func, size_t, Terms&, Targs... comps) { flecs::iter iter_wrapper(iter); func(iter_wrapper, (column< remove_reference_t< remove_pointer_t > >( static_cast< remove_reference_t< remove_pointer_t > *> (comps.ptr), iter->count, comps.is_ref))...); } template = 0> static void invoke_callback(ecs_iter_t *iter, const Func& func, size_t index, Terms& columns, Targs... comps) { invoke_callback(iter, func, index + 1, columns, comps..., columns[index]); } Func m_func; }; //////////////////////////////////////////////////////////////////////////////// //// Utility to invoke callback on entity if it has components in signature //////////////////////////////////////////////////////////////////////////////// template class entity_with_invoker_impl; template class entity_with_invoker_impl> { public: using ColumnArray = flecs::array; using ConstPtrArray = flecs::array; using PtrArray = flecs::array; using DummyArray = flecs::array; using IdArray = flecs::array; template static bool get_ptrs(world& w, ecs_record_t *r, ecs_table_t *table, ArrayType& ptrs) { ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); ecs_type_t type = ecs_table_get_type(table); if (!type) { return false; } /* Get column indices for components */ ColumnArray columns ({ ecs_type_index_of(type, 0, w.id())... }); /* Get pointers for columns for entity */ size_t i = 0; for (int32_t column : columns) { if (column == -1) { return false; } ptrs[i ++] = ecs_record_get_column(r, column, 0); } return true; } template static bool get_mut_ptrs(world& w, ecs_entity_t e, ArrayType& ptrs) { world_t *world = w.c_ptr(); /* Get pointers w/get_mut */ size_t i = 0; DummyArray dummy ({ (ptrs[i ++] = ecs_get_mut_id(world, e, w.id(), NULL), 0)... }); return true; } template static bool invoke_get(world_t *world, entity_t id, const Func& func) { flecs::world w(world); ecs_record_t *r = ecs_record_find(world, id); if (!r) { return false; } ecs_table_t *table = r->table; if (!table) { return false; } ConstPtrArray ptrs; if (!get_ptrs(w, r, table, ptrs)) { return false; } invoke_callback(func, 0, ptrs); return true; } // Utility for storing id in array in pack expansion static size_t store_added(IdArray& added, size_t elem, ecs_table_t *prev, ecs_table_t *next, id_t id) { // Array should only contain ids for components that are actually added, // so check if the prev and next tables are different. if (prev != next) { added[elem] = id; elem ++; } return elem; } template static bool invoke_get_mut(world_t *world, entity_t id, const Func& func) { flecs::world w(world); PtrArray ptrs; // When not deferred take the fast path. if (!w.is_deferred()) { // Bit of low level code so we only do at most one table move & one // entity lookup for the entire operation. // Find table for entity ecs_record_t *r = ecs_record_find(world, id); ecs_table_t *table = NULL; if (r) { table = r->table; } // Find destination table that has all components ecs_table_t *prev = table, *next; size_t elem = 0; IdArray added; // Iterate components, only store added component ids in added array DummyArray dummy_before ({ ( next = ecs_table_add_id(world, prev, w.id()), elem = store_added(added, elem, prev, next, w.id()), prev = next, 0 )... }); (void)dummy_before; // If table is different, move entity straight to it if (table != next) { ecs_ids_t ids; ids.array = added.ptr(); ids.count = static_cast(elem); ecs_commit(world, id, r, next, &ids, NULL); table = next; } if (!get_ptrs(w, r, table, ptrs)) { ecs_abort(ECS_INTERNAL_ERROR, NULL); } // When deferred, obtain pointers with regular get_mut } else { get_mut_ptrs(w, id, ptrs); } invoke_callback(func, 0, ptrs); // Call modified on each component DummyArray dummy_after ({ ( ecs_modified_id(world, id, w.id()), 0)... }); (void)dummy_after; return true; } private: template = 0> static void invoke_callback( const Func& f, size_t, ArrayType&, TArgs&& ... comps) { f(*static_cast::type*>(comps)...); } template = 0> static void invoke_callback(const Func& f, size_t arg, ArrayType& ptrs, TArgs&& ... comps) { invoke_callback(f, arg + 1, ptrs, comps..., ptrs[arg]); } }; template class entity_with_invoker { static_assert(function_traits::value, "type is not callable"); }; template class entity_with_invoker::value > > : public entity_with_invoker_impl< arg_list_t > { static_assert(function_traits::arity > 0, "function must have at least one argument"); }; } // namespace _ } // namespace flecs namespace flecs { template class term_id_builder_i { public: term_id_builder_i() : m_term_id(nullptr) { } virtual ~term_id_builder_i() { } template Base& entity() { ecs_assert(m_term_id != NULL, ECS_INVALID_PARAMETER, NULL); m_term_id->entity = _::cpp_type::id(world()); return *this; } Base& entity(flecs::id_t id) { ecs_assert(m_term_id != NULL, ECS_INVALID_PARAMETER, NULL); m_term_id->entity = id; return *this; } Base& name(const char *name) { ecs_assert(m_term_id != NULL, ECS_INVALID_PARAMETER, NULL); // Const cast is safe, when the value is actually used to construct a // query, it will be duplicated. m_term_id->name = const_cast(name); return *this; } Base& var(flecs::var_kind_t var = flecs::VarIsVariable) { m_term_id->var = static_cast(var); return *this; } Base& var(const char *name) { ecs_assert(m_term_id != NULL, ECS_INVALID_PARAMETER, NULL); // Const cast is safe, when the value is actually used to construct a // query, it will be duplicated. m_term_id->name = const_cast(name); return var(); // Default to VarIsVariable } Base& set(uint8_t mask, const flecs::id_t relation = flecs::IsA) { ecs_assert(m_term_id != NULL, ECS_INVALID_PARAMETER, NULL); m_term_id->set.mask = mask; m_term_id->set.relation = relation; return *this; } Base& superset(const flecs::id_t relation = flecs::IsA, uint8_t mask = 0) { ecs_assert(!(mask & flecs::SubSet), ECS_INVALID_PARAMETER, NULL); return set(flecs::SuperSet | mask, relation); } Base& subset(const flecs::id_t relation = flecs::IsA, uint8_t mask = 0) { ecs_assert(!(mask & flecs::SuperSet), ECS_INVALID_PARAMETER, NULL); return set(flecs::SubSet | mask, relation); } Base& min_depth(int32_t min_depth) { m_term_id->set.min_depth = min_depth; return *this; } Base& max_depth(int32_t max_depth) { m_term_id->set.max_depth = max_depth; return *this; } ecs_term_id_t *m_term_id; protected: virtual flecs::world_t* world() = 0; private: operator Base&() { return *static_cast(this); } }; template class term_builder_i : public term_id_builder_i { public: term_builder_i() : m_term(nullptr) { } term_builder_i(ecs_term_t *term_ptr) { set_term(term_ptr); } template Base& id() { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->pred.entity = _::cpp_type::id(world()); return *this; } template Base& id() { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->pred.entity = _::cpp_type::id(world()); m_term->args[1].entity = _::cpp_type::id(world()); return *this; } template Base& id(id_t o) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->pred.entity = _::cpp_type::id(world()); m_term->args[1].entity = o; return *this; } Base& id(id_t id) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->pred.entity = id; return *this; } Base& id(const flecs::type& type); Base& id(id_t r, id_t o) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->pred.entity = r; m_term->args[1].entity = o; return *this; } Base& expr(const char *expr) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); const char *ptr; if ((ptr = ecs_parse_term(world(), nullptr, expr, expr, m_term)) == nullptr) { ecs_abort(ECS_INVALID_PARAMETER, NULL); } // Should not have more than one term ecs_assert(ptr[0] == 0, ECS_INVALID_PARAMETER, NULL); return *this; } Base& predicate() { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); this->m_term_id = &m_term->pred; return *this; } Base& subject() { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); this->m_term_id = &m_term->args[0]; return *this; } Base& object() { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); this->m_term_id = &m_term->args[1]; return *this; } Base& subject(entity_t entity) { this->subject(); this->m_term_id->entity = entity; return *this; } Base& object(entity_t entity) { this->object(); this->m_term_id->entity = entity; return *this; } template Base& subject() { this->subject(); this->m_term_id->entity = _::cpp_type::id(world()); return *this; } template Base& object() { this->object(); this->m_term_id->entity = _::cpp_type::id(world()); return *this; } Base& role(id_t role) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->role = role; return *this; } Base& inout(flecs::inout_kind_t inout) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->inout = static_cast(inout); return *this; } Base& oper(flecs::oper_kind_t oper) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->oper = static_cast(oper); return *this; } Base& singleton() { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); ecs_assert(m_term->id || m_term->pred.entity, ECS_INVALID_PARAMETER, NULL); flecs::id_t pred = m_term->id; if (!pred) { pred = m_term->pred.entity; } ecs_assert(pred != 0, ECS_INVALID_PARAMETER, NULL); m_term->args[0].entity = pred; return *this; } flecs::id id() { return flecs::id(world(), m_term->id); } flecs::entity get_subject() { return flecs::entity(world(), m_term->args[0].entity); } flecs::entity get_object() { return flecs::entity(world(), m_term->args[1].entity); } flecs::inout_kind_t inout() { return static_cast(m_term->inout); } flecs::oper_kind_t oper() { return static_cast(m_term->oper); } ecs_term_t *m_term; protected: virtual flecs::world_t* world() = 0; void set_term(ecs_term_t *term) { m_term = term; if (term) { this->m_term_id = &m_term->args[0]; // default to subject } else { this->m_term_id = nullptr; } } private: operator Base&() { return *static_cast(this); } }; // Class that describes a term class term final : public term_builder_i { public: term(flecs::world_t *world_ptr) : term_builder_i(&value) , value({}) , m_world(world_ptr) { value.move = true; } term(flecs::world_t *world_ptr, id_t id) : term_builder_i(&value) , value({}) , m_world(world_ptr) { value.move = true; this->id(id); } term(flecs::world_t *world_ptr, ecs_term_t t) : term_builder_i(&value) , value({}) , m_world(world_ptr) { value = t; value.move = false; this->set_term(&value); } term(flecs::world_t *world_ptr, id_t r, id_t o) : term_builder_i(&value) , value({}) , m_world(world_ptr) { value.move = true; this->id(r, o); } term(const term& obj) : term_builder_i(&value) { m_world = obj.m_world; value = ecs_term_copy(&obj.value); this->set_term(&value); } term(term&& obj) : term_builder_i(&value) { m_world = obj.m_world; value = ecs_term_move(&obj.value); obj.reset(); this->set_term(&value); } term& operator=(const term& obj) { ecs_assert(m_world == obj.m_world, ECS_INVALID_PARAMETER, NULL); ecs_term_fini(&value); value = ecs_term_copy(&obj.value); this->set_term(&value); return *this; } term& operator=(term&& obj) { ecs_assert(m_world == obj.m_world, ECS_INVALID_PARAMETER, NULL); ecs_term_fini(&value); value = obj.value; this->set_term(&value); obj.reset(); return *this; } ~term() { ecs_term_fini(&value); } void reset() { value = {}; this->set_term(nullptr); } int finalize() { return ecs_term_finalize(m_world, nullptr, nullptr, &value); } bool is_set() { return ecs_term_is_initialized(&value); } bool is_trivial() { return ecs_term_is_trivial(&value); } ecs_term_t move() { /* explicit move to ecs_term_t */ return ecs_term_move(&value); } ecs_term_t value; protected: flecs::world_t* world() override { return m_world; } private: flecs::world_t *m_world; }; // Filter builder interface template class filter_builder_i : public term_builder_i { public: filter_builder_i(ecs_filter_desc_t *desc, int32_t term_index = 0) : m_term_index(term_index) , m_desc(desc) { } Base& expr(const char *expr) { m_desc->expr = expr; return *this; } Base& substitute_default(bool value = true) { m_desc->substitute_default = value; return *this; } Base& term() { ecs_assert(m_term_index < ECS_TERM_CACHE_SIZE, ECS_INVALID_PARAMETER, NULL); this->set_term(&m_desc->terms[m_term_index]); m_term_index ++; return *this; } Base& arg(int32_t term_index) { ecs_assert(term_index > 0, ECS_INVALID_PARAMETER, NULL); m_term_index = term_index - 1; this->term(); ecs_assert(ecs_term_is_initialized(this->m_term), ECS_INVALID_PARAMETER, NULL); return *this; } template Base& term() { this->term(); *this->m_term = flecs::term(world()).id().move(); return *this; } Base& term(id_t id) { this->term(); *this->m_term = flecs::term(world()).id(id).move(); return *this; } template Base& term() { this->term(); *this->m_term = flecs::term(world()).id().move(); return *this; } template Base& term(id_t o) { this->term(); *this->m_term = flecs::term(world()).id(o).move(); return *this; } Base& term(id_t r, id_t o) { this->term(); *this->m_term = flecs::term(world()).id(r, o).move(); return *this; } Base& term(const flecs::type& type) { this->term(); *this->m_term = flecs::term(world()).id(type).move(); return *this; } Base& term(const char *expr) { this->term(); *this->m_term = flecs::term(world()).expr(expr).move(); return *this; } Base& term(flecs::term& term) { this->term(); *this->m_term = term.move(); return *this; } Base& term(flecs::term&& term) { this->term(); *this->m_term = term.move(); return *this; } void populate_filter_from_pack() { flecs::array ids ({ (_::cpp_type::id(world()))... }); flecs::array inout_kinds ({ (type_to_inout())... }); flecs::array oper_kinds ({ (type_to_oper())... }); size_t i = 0; for (auto id : ids) { this->term(id).inout(inout_kinds[i]).oper(oper_kinds[i]); i ++; } } protected: virtual flecs::world_t* world() = 0; int32_t m_term_index; private: operator Base&() { return *static_cast(this); } template ::value > = 0> constexpr flecs::inout_kind_t type_to_inout() const { return flecs::In; } template ::value > = 0> constexpr flecs::inout_kind_t type_to_inout() const { return flecs::Out; } template ::value || is_reference::value > = 0> constexpr flecs::inout_kind_t type_to_inout() const { return flecs::InOutDefault; } template ::value > = 0> constexpr flecs::oper_kind_t type_to_oper() const { return flecs::Optional; } template ::value > = 0> constexpr flecs::oper_kind_t type_to_oper() const { return flecs::And; } ecs_filter_desc_t *m_desc; }; // Query builder interface template class query_builder_i : public filter_builder_i { using BaseClass = filter_builder_i; public: query_builder_i() : BaseClass(nullptr) , m_desc(nullptr) { } query_builder_i(ecs_query_desc_t *desc, int32_t term_index = 0) : BaseClass(&desc->filter, term_index) , m_desc(desc) { } /** 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 Base& order_by(int(*compare)(flecs::entity_t, const T*, flecs::entity_t, const T*)) { ecs_order_by_action_t cmp = reinterpret_cast(compare); return this->order_by(_::cpp_type::id(world()), cmp); } /** 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. */ Base& order_by(flecs::entity_t component, int(*compare)(flecs::entity_t, const void*, flecs::entity_t, const void*)) { m_desc->order_by = reinterpret_cast(compare); m_desc->order_by_component = component; return *this; } /** 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 Base& group_by(int(*rank)(flecs::world_t*, flecs::entity_t, flecs::type_t type)) { ecs_group_by_action_t rnk = reinterpret_cast(rank); return this->group_by(_::cpp_type::id(this->m_world), rnk); } /** 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. */ Base& group_by(flecs::entity_t component, int(*rank)(flecs::world_t*, flecs::entity_t, flecs::type_t type)) { m_desc->group_by = reinterpret_cast(rank); m_desc->group_by_id = component; return *this; } /** Specify parent query (creates subquery) */ Base& parent(const query_base& parent); protected: virtual flecs::world_t* world() = 0; private: operator Base&() { return *static_cast(this); } ecs_query_desc_t *m_desc; }; // System builder interface template class system_builder_i : public query_builder_i { using BaseClass = query_builder_i; public: system_builder_i() : BaseClass(nullptr) , m_desc(nullptr) , m_add_count(0) { } system_builder_i(ecs_system_desc_t *desc) : BaseClass(&desc->query) , m_desc(desc) , m_add_count(0) { } /** Specify string-based signature. */ Base& signature(const char *signature) { m_desc->query.filter.expr = signature; return *this; } /** Specify when the system should be ran. * Use this function to set in which phase the system should run or whether * the system is reactive. Valid values for reactive systems are: * * flecs::OnAdd * flecs::OnRemove * flecs::OnSet * flecs::UnSet * * @param kind The kind that specifies when the system should be ran. */ Base& kind(entity_t kind) { m_desc->entity.add[0] = kind; return *this; } /** Set system interval. * This operation will cause the system to be ran at the specified interval. * * The timer is synchronous, and is incremented each frame by delta_time. * * @param interval The interval value. */ Base& interval(FLECS_FLOAT interval) { m_desc->interval = interval; return *this; } /** Set system rate. * This operation will cause the system to be ran at a multiple of the * provided tick source. The tick source may be any entity, including * another system. * * @param tick_source The tick source. * @param rate The multiple at which to run the system. */ Base& rate(const entity_t tick_source, int32_t rate) { m_desc->rate = rate; m_desc->tick_source = tick_source; return *this; } /** Set system rate. * This operation will cause the system to be ran at a multiple of the * frame tick frequency. If a tick source was provided, this just updates * the rate of the system. * * @param rate The multiple at which to run the system. */ Base& rate(int32_t rate) { m_desc->rate = rate; return *this; } /** System is an on demand system */ Base& on_demand() { m_desc->entity.add[m_add_count ++] = flecs::OnDemand; return *this; } /** System is a hidden system */ Base& hidden() { m_desc->entity.add[m_add_count ++] = flecs::Hidden; return *this; } /** Associate system with entity */ Base& self(flecs::entity self) { m_desc->self = self; return *this; } /** Set system context */ Base& ctx(void *ptr) { m_desc->ctx = ptr; return *this; } ECS_DEPRECATED("use interval") Base& period(FLECS_FLOAT period) { return this->interval(period); } ECS_DEPRECATED("use ctx") Base& set_context(void *ptr) { ctx(ptr); return *this; } protected: virtual flecs::world_t* world() = 0; private: operator Base&() { return *static_cast(this); } ecs_system_desc_t *m_desc; int32_t m_add_count; }; // Observer builder interface template class observer_builder_i : public filter_builder_i { using BaseClass = filter_builder_i; public: observer_builder_i() : BaseClass(nullptr) , m_desc(nullptr) , m_event_count(0) { } observer_builder_i(ecs_observer_desc_t *desc) : BaseClass(&desc->filter) , m_desc(desc) , m_event_count(0) { } /** Specify when the system should be ran. * Use this function to set in which phase the system should run or whether * the system is reactive. Valid values for reactive systems are: * * flecs::OnAdd * flecs::OnRemove * flecs::OnSet * flecs::UnSet * * @param kind The kind that specifies when the system should be ran. */ Base& event(entity_t kind) { m_desc->events[m_event_count ++] = kind; return *this; } /** Associate observer with entity */ Base& self(flecs::entity self) { m_desc->self = self; return *this; } /** Set system context */ Base& ctx(void *ptr) { m_desc->ctx = ptr; return *this; } protected: virtual flecs::world_t* world() = 0; private: operator Base&() { return *static_cast(this); } ecs_observer_desc_t *m_desc; int32_t m_event_count; }; // Filter builder template class filter_builder_base : public filter_builder_i, Components ...> { public: filter_builder_base(flecs::world_t *world) : filter_builder_i, Components ...>(&m_desc) , m_desc({}) , m_world(world) { this->populate_filter_from_pack(); } filter_builder_base(const filter_builder_base& obj) : filter_builder_i, Components ...>(&m_desc, obj.m_term_index) { m_world = obj.m_world; m_desc = obj.m_desc; } filter_builder_base(filter_builder_base&& obj) : filter_builder_i, Components ...>(&m_desc, obj.m_term_index) { m_world = obj.m_world; m_desc = obj.m_desc; } operator filter() const; operator ecs_filter_t() const { ecs_filter_t f; int res = ecs_filter_init(this->m_world, &f, &this->m_desc); if (res != 0) { ecs_abort(ECS_INVALID_PARAMETER, NULL); } return f; } filter build() const; ecs_filter_desc_t m_desc; flecs::world_t* world() override { return m_world; } protected: flecs::world_t *m_world; }; template class filter_builder final : public filter_builder_base { public: filter_builder(flecs::world_t *world) : filter_builder_base(world) { } operator filter<>() const; }; // Query builder template class query_builder_base : public query_builder_i, Components ...> { public: query_builder_base(flecs::world_t *world) : query_builder_i, Components ...>(&m_desc) , m_desc({}) , m_world(world) { this->populate_filter_from_pack(); } query_builder_base(const query_builder_base& obj) : query_builder_i, Components ...>(&m_desc, obj.m_term_index) { m_world = obj.m_world; m_desc = obj.m_desc; } query_builder_base(query_builder_base&& obj) : query_builder_i, Components ...>(&m_desc, obj.m_term_index) { m_world = obj.m_world; m_desc = obj.m_desc; } operator query() const; operator ecs_query_t*() const { return ecs_query_init(this->m_world, &this->m_desc); } query build() const; ecs_query_desc_t m_desc; flecs::world_t* world() override { return m_world; } protected: flecs::world_t *m_world; }; template class query_builder final : public query_builder_base { public: query_builder(flecs::world_t *world) : query_builder_base(world) { } operator query<>() const; }; template class system_builder final : public system_builder_i, Components ...> { using Class = system_builder; public: explicit system_builder(flecs::world_t* world, const char *name = nullptr, const char *expr = nullptr) : system_builder_i(&m_desc) , m_desc({}) , m_world(world) { m_desc.entity.name = name; m_desc.entity.sep = "::"; m_desc.entity.add[0] = flecs::OnUpdate; m_desc.query.filter.expr = expr; this->populate_filter_from_pack(); } // put using outside of action so we can still use it without it being // flagged as deprecated. template using action_invoker_t = typename _::iter_invoker< typename std::decay::type, Components...>; template ECS_DEPRECATED("use each or iter") system action(Func&& func) const; /* 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) const; /* Each is similar to action, but accepts a function that operates on a * single entity */ template system each(Func&& func) const; ecs_system_desc_t m_desc; protected: flecs::world_t* world() override { return m_world; } flecs::world_t *m_world; private: template entity_t build(Func&& func, bool is_each) const { auto ctx = FLECS_NEW(Invoker)(std::forward(func)); entity_t e, kind = m_desc.entity.add[0]; bool is_trigger = kind == flecs::OnAdd || kind == flecs::OnRemove; if (is_trigger) { ecs_trigger_desc_t desc = {}; ecs_term_t term = m_desc.query.filter.terms[0]; if (ecs_term_is_initialized(&term)) { desc.term = term; } else { desc.expr = m_desc.query.filter.expr; } desc.entity.entity = m_desc.entity.entity; desc.events[0] = kind; desc.callback = Invoker::run; desc.self = m_desc.self; desc.ctx = m_desc.ctx; desc.binding_ctx = ctx; desc.binding_ctx_free = reinterpret_cast< ecs_ctx_free_t>(_::free_obj); e = ecs_trigger_init(m_world, &desc); } else { ecs_system_desc_t desc = m_desc; desc.callback = Invoker::run; desc.self = m_desc.self; desc.query.filter.substitute_default = is_each; desc.binding_ctx = ctx; desc.binding_ctx_free = reinterpret_cast< ecs_ctx_free_t>(_::free_obj); e = ecs_system_init(m_world, &desc); } return e; } }; template class observer_builder final : public observer_builder_i, Components ...> { using Class = observer_builder; public: explicit observer_builder(flecs::world_t* world, const char *name = nullptr, const char *expr = nullptr) : observer_builder_i(&m_desc) , m_desc({}) , m_world(world) { m_desc.entity.name = name; m_desc.entity.sep = "::"; m_desc.entity.add[0] = flecs::OnUpdate; m_desc.filter.expr = expr; this->populate_filter_from_pack(); } /* 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 observer iter(Func&& func) const; /* Each is similar to action, but accepts a function that operates on a * single entity */ template observer each(Func&& func) const; ecs_observer_desc_t m_desc; protected: flecs::world_t* world() override { return m_world; } flecs::world_t *m_world; private: template entity_t build(Func&& func, bool is_each) const { auto ctx = FLECS_NEW(Invoker)(std::forward(func)); ecs_observer_desc_t desc = m_desc; desc.callback = Invoker::run; desc.filter.substitute_default = is_each; desc.binding_ctx = ctx; desc.binding_ctx_free = reinterpret_cast< ecs_ctx_free_t>(_::free_obj); return ecs_observer_init(m_world, &desc); } }; } #ifdef FLECS_DEPRECATED namespace flecs { /* Deprecated functions */ template class type_deprecated { public: template ECS_DEPRECATED("use add") type& add_trait() { static_cast(this)->add(ecs_pair( _::cpp_type::id(world()), _::cpp_type::id(world()))); return *base(); } template ECS_DEPRECATED("use add(const flecs::entity&)") type& add_trait(const flecs::entity& c) { static_cast(this)->add(ecs_pair(_::cpp_type::id(world()), c.id())); return *base(); } ECS_DEPRECATED("use add(const flecs::entity&, const flecs::entity&)") type& add_trait(const flecs::entity& t, const flecs::entity& c) { static_cast(this)->add(ecs_pair(t.id(), c.id())); return *base(); } template ECS_DEPRECATED("use add_object(const flecs::entity&)") type& add_trait_tag(const flecs::entity& t) { static_cast(this)->add(ecs_pair(t.id(), _::cpp_type::id(world()))); return *base(); } private: Base* base() { return static_cast(this); } flecs::world_t* world() { return base()->world().c_ptr(); } }; } #else template class type_deprecated { }; #endif namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// A collection of component ids used to describe the contents of a table //////////////////////////////////////////////////////////////////////////////// template class type_base : public type_deprecated { public: explicit type_base( world_t *world, const char *name = nullptr, const char *expr = nullptr) { ecs_type_desc_t desc = {}; desc.entity.name = name; desc.ids_expr = expr; m_entity = flecs::entity(world, ecs_type_init(world, &desc)); sync_from_flecs(); } explicit type_base(world_t *world, type_t t) : m_entity( world, static_cast(0) ) , m_type( t ) , m_normalized( t ) { } type_base(type_t t) : m_type( t ) , m_normalized( t ) { } Base& add(const Base& t) { m_type = ecs_type_add(world(), m_type, t.id()); m_normalized = ecs_type_merge(world(), m_normalized, t, nullptr); sync_from_me(); return *this; } Base& add(id_t e) { m_type = ecs_type_add(world(), m_type, e); m_normalized = ecs_type_add(world(), m_normalized, e); sync_from_me(); return *this; } template Base& add() { return this->add(_::cpp_type::id(world())); } Base& add(entity_t relation, entity_t object) { return this->add(ecs_pair(relation, object)); } template Base& add() { return this->add(_::cpp_type::id(world())); } Base& is_a(entity_t object) { return this->add(flecs::IsA, object); } Base& child_of(entity_t object) { return this->add(flecs::ChildOf, object); } template Base& add(entity_t object) { return this->add(_::cpp_type::id(world()), object); } template Base& add_w_object(entity_t relation) { return this->add(relation, _::cpp_type::id(world())); } bool has(id_t id) { return ecs_type_has_id(world(), m_normalized, id, false); } bool has(id_t relation, id_t object) { return ecs_type_has_id(world(), m_normalized, ecs_pair(relation, object), false); } template bool has() { return this->has(_::cpp_type::id(world())); } template bool has() { return this->has(_::cpp_type>::id(world())); } template Base& component() { component_for_id(world(), m_entity); return *this; } flecs::string str() const { char *str = ecs_type_str(world(), m_type); return flecs::string(str); } type_t c_ptr() const { return m_type; } flecs::id_t id() const { return m_entity.id(); } flecs::entity entity() const { return m_entity; } flecs::world world() const { return m_entity.world(); } type_t c_normalized() const { return m_normalized; } void enable() const { ecs_enable(world(), id(), true); } void disable() const { ecs_enable(world(), id(), false); } flecs::vector vector() { return flecs::vector( const_cast(m_normalized)); } flecs::id get(int32_t index) { return flecs::id(world(), vector().get(index)); } /* Implicit conversion to type_t */ operator type_t() const { return m_normalized; } operator Base&() { return *static_cast(this); } private: void sync_from_me() { if (!id()) { return; } EcsType *tc = ecs_get_mut(world(), id(), EcsType, NULL); ecs_assert(tc != NULL, ECS_INTERNAL_ERROR, NULL); tc->type = m_type; tc->normalized = m_normalized; ecs_modified(world(), id(), EcsType); } void sync_from_flecs() { if (!id()) { return; } EcsType *tc = ecs_get_mut(world(), id(), EcsType, NULL); ecs_assert(tc != NULL, ECS_INTERNAL_ERROR, NULL); m_type = tc->type; m_normalized = tc->normalized; ecs_modified(world(), id(), EcsType); } flecs::entity m_entity; type_t m_type; type_t m_normalized; }; class type : public type_base { public: explicit type( world_t *world, const char *name = nullptr, const char *expr = nullptr) : type_base(world, name, expr) { } explicit type(world_t *world, type_t t) : type_base(world, t) { } type(type_t t) : type_base(t) { } }; class pipeline : public type_base { public: explicit pipeline(world_t *world, const char *name) : type_base(world, name) { this->entity().add(flecs::Pipeline); } }; } // namespace flecs namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// 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_add_module_tag(world, result.id()); ecs_set_scope(world.c_ptr(), result.id()); // Only register copy/move/dtor, make sure to not instantiate ctor as the // default ctor doesn't work for modules. Additionally, the module ctor // should only be invoked once per import. EcsComponentLifecycle cl{}; cl.copy = _::copy(); cl.move = _::move(); cl.dtor = _::dtor(); ecs_set_component_actions_w_entity(world, result, &cl); return result; } //////////////////////////////////////////////////////////////////////////////// //// Import a module //////////////////////////////////////////////////////////////////////////////// template ecs_entity_t do_import(world& world, const char *symbol) { ecs_trace_1("import %s", _::name_helper::name()); ecs_log_push(); ecs_entity_t scope = ecs_get_scope(world); // Create custom storage to prevent object destruction T* module_data = static_cast(ecs_os_malloc(sizeof(T))); FLECS_PLACEMENT_NEW(module_data, T(world)); ecs_set_scope(world, scope); // It should now be possible to lookup the module ecs_entity_t m = ecs_lookup_symbol(world, symbol, true); ecs_assert(m != 0, ECS_MODULE_UNDEFINED, symbol); _::cpp_type::init(world, m, false); ecs_assert(_::cpp_type::size() != 0, ECS_INTERNAL_ERROR, NULL); // Set module singleton component T* module_ptr = static_cast( ecs_get_mut_id(world, m, _::cpp_type::id_explicit(world, nullptr, false), NULL)); *module_ptr = std::move(*module_data); // Don't dtor, as a module should only be destructed once when the module // component is removed. ecs_os_free(module_data); // Add module tag ecs_add_id(world, m, flecs::Module); ecs_log_pop(); return m; } template flecs::entity import(world& world) { char *symbol = _::symbol_helper::symbol(); ecs_entity_t m = ecs_lookup_symbol(world.c_ptr(), symbol, true); if (!_::cpp_type::registered()) { /* Module is registered with world, initialize static data */ if (m) { _::cpp_type::init(world.c_ptr(), m, false); /* Module is not yet registered, register it now */ } else { m = do_import(world, symbol); } /* Module has been registered, but could have been for another world. Import * if module hasn't been registered for this world. */ } else if (!m) { m = do_import(world, symbol); } ecs_os_free(symbol); return flecs::entity(world, m); } } // namespace flecs namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// Ad hoc queries (filters) //////////////////////////////////////////////////////////////////////////////// class filter_base { public: filter_base() : m_world(nullptr) , m_filter({}) { } filter_base(world_t *world, ecs_filter_t *filter = NULL) : m_world(world) { ecs_filter_move(&m_filter, filter); } /** Get pointer to C filter object. */ const filter_t* c_ptr() const { if (m_filter.term_count) { return &m_filter; } else { return NULL; } } filter_base(const filter_base& obj) { this->m_world = obj.m_world; ecs_filter_copy(&m_filter, &obj.m_filter); } filter_base& operator=(const filter_base& obj) { this->m_world = obj.m_world; ecs_filter_copy(&m_filter, &obj.m_filter); return *this; } filter_base(filter_base&& obj) { this->m_world = obj.m_world; ecs_filter_move(&m_filter, &obj.m_filter); } filter_base& operator=(filter_base&& obj) { this->m_world = obj.m_world; ecs_filter_move(&m_filter, &obj.m_filter); return *this; } /** Free the filter. */ ~filter_base() { ecs_filter_fini(&m_filter); } template void iter(Func&& func) const { ecs_iter_t it = ecs_filter_iter(m_world, &m_filter); while (ecs_filter_next(&it)) { _::iter_invoker(func).invoke(&it); } } template void each_term(const Func& func) { for (int i = 0; i < m_filter.term_count; i ++) { flecs::term t(m_world, m_filter.terms[i]); func(t); } } flecs::term term(int32_t index) { return flecs::term(m_world, m_filter.terms[index]); } int32_t term_count() { return m_filter.term_count; } flecs::string str() { char *result = ecs_filter_str(m_world, &m_filter); return flecs::string(result); } protected: world_t *m_world; filter_t m_filter; }; template class filter : public filter_base { using Terms = typename _::term_ptrs::array; public: filter() { } filter(world_t *world, filter_t *f) : filter_base(world, f) { } explicit filter(const world& world, const char *expr = nullptr) : filter_base(world.c_ptr()) { auto qb = world.filter_builder() .expr(expr); if (!expr) { qb.substitute_default(); } flecs::filter_t f = qb; ecs_filter_move(&m_filter, &f); } filter(const filter& obj) : filter_base(obj) { } filter& operator=(const filter& obj) { *this = obj; return *this; } filter(filter&& obj) : filter_base(std::move(obj)) { } filter& operator=(filter&& obj) { filter_base(std::move(obj)); return *this; } template void each(Func&& func) const { iterate<_::each_invoker>(std::forward(func), ecs_filter_next); } template void iter(Func&& func) const { iterate<_::iter_invoker>(std::forward(func), ecs_filter_next); } private: template < template class Invoker, typename Func, typename NextFunc, typename ... Args> void iterate(Func&& func, NextFunc next, Args &&... args) const { ecs_iter_t it = ecs_filter_iter(m_world, &m_filter); while (next(&it, std::forward(args)...)) { Invoker(std::move(func)).invoke(&it); } } }; } namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// 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()); } template void take(const F& f) { if (m_snapshot) { ecs_snapshot_free(m_snapshot); } ecs_iter_t it = ecs_filter_iter(m_world, f.c_ptr()); printf("filter = %s\n", ecs_filter_str(m_world, f.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; } filter_iterator begin(); filter_iterator end(); private: const world& m_world; snapshot_t *m_snapshot; }; } // namespace flecs namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// 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, ecs_iter_next_action_t action) : m_world( world.c_ptr() ) , m_iter( ecs_filter_iter(m_world, NULL) ) , m_action(action) { m_has_next = m_action(&m_iter); } filter_iterator(const world& world, const snapshot& snapshot, ecs_iter_next_action_t action) : m_world( world.c_ptr() ) , m_iter( ecs_snapshot_iter(snapshot.c_ptr(), NULL) ) , 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::world_t *world, const flecs::entity_t entity) : m_iter( ecs_scope_iter(world, entity )) { 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) : m_world( world ) { } inline filter_iterator begin() const { return filter_iterator(m_world, ecs_filter_next); } inline filter_iterator end() const { return filter_iterator(ecs_filter_next); } private: const world& m_world; }; //////////////////////////////////////////////////////////////////////////////// //// Utility for creating a snapshot-based filter iterator //////////////////////////////////////////////////////////////////////////////// class snapshot_filter { public: snapshot_filter(const world& world, const snapshot& snapshot) : m_world( world ) , m_snapshot( snapshot ) { } inline filter_iterator begin() const { return filter_iterator(m_world, m_snapshot, ecs_snapshot_next); } inline filter_iterator end() const { return filter_iterator(ecs_snapshot_next); } private: const world& m_world; const snapshot& m_snapshot; }; //////////////////////////////////////////////////////////////////////////////// //// Utility for creating a child table iterator //////////////////////////////////////////////////////////////////////////////// class child_iterator { public: child_iterator(const flecs::entity_view& entity) : m_world( entity.world().c_ptr() ) , m_parent( entity.id() ) { } inline tree_iterator begin() const { return tree_iterator(m_world, m_parent); } inline tree_iterator end() const { return tree_iterator(); } private: flecs::world_t *m_world; flecs::entity_t m_parent; }; //////////////////////////////////////////////////////////////////////////////// //// Filter fwd declared functions //////////////////////////////////////////////////////////////////////////////// inline filter_iterator snapshot::begin() { return filter_iterator(m_world, *this, ecs_snapshot_next); } inline filter_iterator snapshot::end() { return filter_iterator(ecs_snapshot_next); } } namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// Persistent queries //////////////////////////////////////////////////////////////////////////////// class query_base { public: query_base() : m_world(nullptr) , m_query(nullptr) { } query_base(world_t *world, query_t *query = nullptr) : m_world(world) , m_query(query) { } /** 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::_::cpp_type::id(m_world), reinterpret_cast(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(ecs_group_by_action_t callback) { ecs_query_group_by(m_world, m_query, flecs::_::cpp_type::id(m_world), callback); } /** 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, ecs_group_by_action_t callback) { ecs_query_group_by(m_world, m_query, component.id(), callback); } /** 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; } template void iter(Func&& func) const { ecs_iter_t it = ecs_query_iter(m_query); while (ecs_query_next(&it)) { _::iter_invoker(func).invoke(&it); } } template void each_term(const Func& func) { const ecs_filter_t *f = ecs_query_get_filter(m_query); ecs_assert(f != NULL, ECS_INVALID_PARAMETER, NULL); for (int i = 0; i < f->term_count; i ++) { flecs::term t(m_world, f->terms[i]); func(t); } } flecs::term term(int32_t index) { const ecs_filter_t *f = ecs_query_get_filter(m_query); ecs_assert(f != NULL, ECS_INVALID_PARAMETER, NULL); return flecs::term(m_world, f->terms[index]); } int32_t term_count() { const ecs_filter_t *f = ecs_query_get_filter(m_query); return f->term_count; } flecs::string str() { const ecs_filter_t *f = ecs_query_get_filter(m_query); char *result = ecs_filter_str(m_world, f); return flecs::string(result); } protected: world_t *m_world; query_t *m_query; }; template class query : public query_base { using Terms = typename _::term_ptrs::array; public: query() { } query(world_t *world, query_t *q) : query_base(world, q) { } explicit query(const world& world, const char *expr = nullptr) : query_base(world.c_ptr()) { auto qb = world.query_builder() .expr(expr); if (!expr) { qb.substitute_default(); } m_query = qb; } explicit query(const world& world, query_base& parent, const char *expr = nullptr) : query_base(world.c_ptr()) { auto qb = world.query_builder() .parent(parent) .expr(expr); if (!expr) { qb.substitute_default(); } m_query = qb; } template void each(Func&& func) const { iterate<_::each_invoker>(std::forward(func), ecs_query_next); } template void each_worker(int32_t stage_current, int32_t stage_count, Func&& func) const { iterate<_::each_invoker>(std::forward(func), ecs_query_next_worker, stage_current, stage_count); } template void iter(Func&& func) const { iterate<_::iter_invoker>(std::forward(func), ecs_query_next); } template void iter_worker(int32_t stage_current, int32_t stage_count, Func&& func) const { iterate<_::iter_invoker>(std::forward(func), ecs_query_next_worker, stage_current, stage_count); } template ECS_DEPRECATED("use each or iter") void action(Func&& func) const { iterate<_::action_invoker>(std::forward(func), ecs_query_next); } private: template < template class Invoker, typename Func, typename NextFunc, typename ... Args> void iterate(Func&& func, NextFunc next, Args &&... args) const { ecs_iter_t it = ecs_query_iter(m_query); while (next(&it, std::forward(args)...)) { Invoker(func).invoke(&it); } } }; } // namespace flecs namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// Fluent interface to run a system manually //////////////////////////////////////////////////////////////////////////////// class system_runner_fluent { public: system_runner_fluent( world_t *world, entity_t id, int32_t stage_current, int32_t stage_count, FLECS_FLOAT delta_time, void *param) : m_stage(world) , m_id(id) , m_delta_time(delta_time) , m_param(param) , m_filter() , m_offset(0) , m_limit(0) , m_stage_current(stage_current) , m_stage_count(stage_count) { } template system_runner_fluent& filter(const F& f) { m_filter = f; return *this; } system_runner_fluent& offset(int32_t offset) { m_offset = offset; return *this; } system_runner_fluent& limit(int32_t limit) { m_limit = limit; return *this; } system_runner_fluent& stage(flecs::world& stage) { m_stage = stage.c_ptr(); return *this; } ~system_runner_fluent() { if (m_stage_count) { ecs_run_worker( m_stage, m_id, m_stage_current, m_stage_count, m_delta_time, m_param); } else { ecs_run_w_filter( m_stage, m_id, m_delta_time, m_offset, m_limit, m_filter.c_ptr(), m_param); } } private: world_t *m_stage; entity_t m_id; FLECS_FLOAT m_delta_time; void *m_param; flecs::filter<> m_filter; int32_t m_offset; int32_t m_limit; int32_t m_stage_current; int32_t m_stage_count; }; //////////////////////////////////////////////////////////////////////////////// //// Register a system with Flecs //////////////////////////////////////////////////////////////////////////////// template class system : public entity { public: explicit system() : entity() { } explicit system(flecs::world_t *world, flecs::entity_t id) : entity(world, id) { } template void order_by(int(*compare)(flecs::entity_t, const T*, flecs::entity_t, const T*)) { this->order_by(flecs::_::cpp_type::id(m_world), reinterpret_cast(compare)); } void order_by(flecs::entity_t comp, int(*compare)(flecs::entity_t, const void*, flecs::entity_t, const void*)) { ecs_query_t *q = query().c_ptr(); ecs_assert(q != NULL, ECS_INVALID_PARAMETER, NULL); ecs_query_order_by(m_world, q, comp, compare); } template void group_by(int(*rank)(flecs::world_t*, flecs::entity_t, flecs::type_t type)) { this->group_by(flecs::_::cpp_type::id(m_world), rank); } void group_by(flecs::entity_t comp, int(*rank)(flecs::world_t*, flecs::entity_t, flecs::type_t type)) { ecs_query_t *q = query().c_ptr(); ecs_assert(q != NULL, ECS_INVALID_PARAMETER, NULL); ecs_query_group_by(m_world, q, comp, rank); } /** Set system interval. * This operation will cause the system to be ran at the specified interval. * * The timer is synchronous, and is incremented each frame by delta_time. * * @param interval The interval value. */ void interval(FLECS_FLOAT interval) { ecs_set_interval(m_world, m_id, interval); } /** Set system rate. * This operation will cause the system to be ran at a multiple of the * provided tick source. The tick source may be any entity, including * another system. * * @param tick_source The tick source. * @param rate The multiple at which to run the system. */ void rate(const flecs::entity& tick_source, int32_t rate) { ecs_set_rate(m_world, m_id, rate, tick_source.id()); } /** Set system rate. * This operation will cause the system to be ran at a multiple of the * frame tick frequency. If a tick source was provided, this just updates * the rate of the system. * * @param rate The multiple at which to run the system. */ void rate(int32_t rate) { ecs_set_rate(m_world, m_id, rate, 0); } /** Get interval. * Get interval at which the system is running. * * @return The timer entity. */ FLECS_FLOAT interval() { return ecs_get_interval(m_world, m_id); } void enable() { ecs_enable(m_world, m_id, true); } void disable() { ecs_enable(m_world, m_id, false); } void ctx(void *ctx) { if (ecs_has(m_world, m_id, EcsSystem)) { ecs_system_desc_t desc = {}; desc.entity.entity = m_id; desc.ctx = ctx; ecs_system_init(m_world, &desc); } else { ecs_trigger_desc_t desc = {}; desc.entity.entity = m_id; desc.ctx = ctx; ecs_trigger_init(m_world, &desc); } } void* ctx() const { if (ecs_has(m_world, m_id, EcsSystem)) { return ecs_get_system_ctx(m_world, m_id); } else { return ecs_get_trigger_ctx(m_world, m_id); } } ECS_DEPRECATED("use interval") void period(FLECS_FLOAT period) { this->interval(period); } ECS_DEPRECATED("use interval") void set_period(FLECS_FLOAT period) const { this->interval(period); } ECS_DEPRECATED("use ctx(void*)") void set_context(void *ptr) { ctx(ptr); } ECS_DEPRECATED("use void* ctx()") void* get_context() const { return ctx(); } query_base query() const { return query_base(m_world, ecs_get_system_query(m_world, m_id)); } system_runner_fluent run(FLECS_FLOAT delta_time = 0.0f, void *param = nullptr) const { return system_runner_fluent(m_world, m_id, 0, 0, delta_time, param); } system_runner_fluent run_worker( int32_t stage_current, int32_t stage_count, FLECS_FLOAT delta_time = 0.0f, void *param = nullptr) const { return system_runner_fluent( m_world, m_id, stage_current, stage_count, delta_time, param); } }; } // namespace flecs namespace flecs { template class observer : public entity { public: explicit observer() : entity() { } explicit observer(flecs::world_t *world, flecs::entity_t id) : entity(world, id) { } void ctx(void *ctx) { ecs_observer_desc_t desc = {}; desc.entity.entity = m_id; desc.ctx = ctx; ecs_observer_init(m_world, &desc); } void* ctx() const { return ecs_get_observer_ctx(m_world, m_id); } }; } // namespace flecs namespace flecs { namespace _ { template inline void ctor_world_entity_impl( ecs_world_t* world, ecs_entity_t, const ecs_entity_t* ids, void *ptr, size_t size, int32_t count, void*) { (void)size; ecs_assert(size == sizeof(T), ECS_INTERNAL_ERROR, NULL); T *arr = static_cast(ptr); flecs::world w(world); for (int i = 0; i < count; i ++) { flecs::entity e(world, ids[i]); FLECS_PLACEMENT_NEW(&arr[i], T(w, e)); } } } // _ } // flecs namespace flecs { inline flecs::entity id::entity() const { ecs_assert(!is_pair(), ECS_INVALID_OPERATION, NULL); ecs_assert(!role(), ECS_INVALID_OPERATION, NULL); return flecs::entity(m_world, m_id); } inline flecs::entity id::role() const { return flecs::entity(m_world, m_id & ECS_ROLE_MASK); } inline flecs::entity id::relation() const { ecs_assert(is_pair(), ECS_INVALID_OPERATION, NULL); flecs::entity_t e = ECS_PAIR_RELATION(m_id); if (m_world) { return flecs::entity(m_world, ecs_get_alive(m_world, e)); } else { return flecs::entity(e); } } inline flecs::entity id::object() const { flecs::entity_t e = ECS_PAIR_OBJECT(m_id); if (m_world) { return flecs::entity(m_world, ecs_get_alive(m_world, e)); } else { return flecs::entity(m_world, e); } } inline flecs::entity id::add_role(flecs::id_t role) const { return flecs::entity(m_world, m_id | role); } inline flecs::entity id::remove_role(flecs::id_t role) const { (void)role; ecs_assert((m_id & ECS_ROLE_MASK) == role, ECS_INVALID_PARAMETER, NULL); return flecs::entity(m_world, m_id & ECS_COMPONENT_MASK); } inline flecs::entity id::remove_role() const { return flecs::entity(m_world, m_id & ECS_COMPONENT_MASK); } inline flecs::entity id::remove_generation() const { return flecs::entity(m_world, static_cast(m_id)); } inline entity id::lo() const { return flecs::entity(m_world, ecs_entity_t_lo(m_id)); } inline entity id::hi() const { return flecs::entity(m_world, ecs_entity_t_hi(m_id)); } inline entity id::comb(entity_view lo, entity_view hi) { return flecs::entity(lo.world(), ecs_entity_t_comb(lo.id(), hi.id())); } } namespace flecs { //////////////////////////////////////////////////////////////////////////////// //// Entity range, allows for operating on a range of consecutive entities //////////////////////////////////////////////////////////////////////////////// class ECS_DEPRECATED("do not use") entity_range final { public: entity_range(const world& world, int32_t count) : m_world(world.c_ptr()) , m_ids( ecs_bulk_new_w_type(m_world, nullptr, count)) { } entity_range(const world& world, int32_t count, flecs::type type) : m_world(world.c_ptr()) , m_ids( ecs_bulk_new_w_type(m_world, type.c_ptr(), count)) { } private: world_t *m_world; const entity_t *m_ids; }; template flecs::entity ref::entity() const { return flecs::entity(m_world, m_entity); } template inline const Base& entity_builder::add(const type& type) const { ecs_add_type(this->base_world(), this->base_id(), type.c_ptr()); return *this; } template inline const Base& entity_builder::remove(const type& type) const { ecs_remove_type(this->base_world(), this->base_id(), type.c_ptr()); return *this; } template inline const Base& entity_builder::add_owned(const type& type) const { return add_owned(type.id()); } template inline const Base& entity_builder::add_switch(const type& sw) const { return add_switch(sw.id()); } template inline const Base& entity_builder::remove_switch(const type& sw) const { return remove_switch(sw.id()); } template template ::value > > inline const Base& entity_builder::set(const Func& func) const { _::entity_with_invoker::invoke_get_mut( this->base_world(), this->base_id(), func); return *this; } template template inline const Base& entity_builder::component() const { component_for_id(this->base_world(), this->base_id()); return *this; } inline bool entity_view::has_switch(const flecs::type& type) const { return ecs_has_entity(m_world, m_id, flecs::Switch | type.id()); } inline flecs::entity entity_view::get_case(const flecs::type& sw) const { return flecs::entity(m_world, ecs_get_case(m_world, m_id, sw.id())); } inline flecs::entity entity_view::get_case(flecs::id_t sw) const { return flecs::entity(m_world, ecs_get_case(m_world, m_id, sw)); } template inline flecs::entity entity_view::get_case() const { return get_case(_::cpp_type::id(m_world)); } inline flecs::entity entity_view::get_object( flecs::entity_t relation, int32_t index) const { return flecs::entity(m_world, ecs_get_object(m_world, m_id, relation, index)); } inline flecs::entity entity_view::mut(const flecs::world& stage) const { ecs_assert(!stage.is_readonly(), ECS_INVALID_PARAMETER, "cannot use readonly world/stage to create mutable handle"); return flecs::entity(m_id).set_stage(stage.c_ptr()); } /** Same as mut(world), but for iterator. * This operation allows for the construction of a mutable entity handle * from an iterator. * * @param stage An created for the current stage. * @return An entity handle that allows for mutations in the current stage. */ inline flecs::entity entity_view::mut(const flecs::iter& it) const { ecs_assert(!it.world().is_readonly(), ECS_INVALID_PARAMETER, "cannot use iterator created for readonly world/stage to create mutable handle"); return flecs::entity(m_id).set_stage(it.world().c_ptr()); } /** Same as mut(world), but for entity. * This operation allows for the construction of a mutable entity handle * from another entity. This is useful in each() functions, which only * provide a handle to the entity being iterated over. * * @param stage An created for the current stage. * @return An entity handle that allows for mutations in the current stage. */ inline flecs::entity entity_view::mut(const flecs::entity_view& e) const { ecs_assert(!e.world().is_readonly(), ECS_INVALID_PARAMETER, "cannot use entity created for readonly world/stage to create mutable handle"); return flecs::entity(m_id).set_stage(e.m_world); } inline flecs::entity entity_view::set_stage(world_t *stage) { m_world = stage; return flecs::entity(m_world, m_id); } inline flecs::type entity_view::type() const { return flecs::type(m_world, ecs_get_type(m_world, m_id)); } inline flecs::type entity_view::to_type() const { ecs_type_t type = ecs_type_from_id(m_world, m_id); return flecs::type(m_world, type); } inline child_iterator entity_view::children() const { ecs_assert(m_id != 0, ECS_INVALID_PARAMETER, NULL); return flecs::child_iterator(*this); } template inline void entity_view::each(const Func& func) const { const ecs_vector_t *type = ecs_get_type(m_world, m_id); if (!type) { return; } const ecs_id_t *ids = static_cast( _ecs_vector_first(type, ECS_VECTOR_T(ecs_id_t))); int32_t count = ecs_vector_count(type); for (int i = 0; i < count; i ++) { ecs_id_t id = ids[i]; flecs::id ent(m_world, id); func(ent); // Case is not stored in type, so handle separately if ((id & ECS_ROLE_MASK) == flecs::Switch) { ent = flecs::id( m_world, flecs::Case | ecs_get_case( m_world, m_id, ent.object().id())); func(ent); } } } template inline void entity_view::match(id_t pattern, const Func& func) const { const ecs_vector_t *type = ecs_get_type(m_world, m_id); if (!type) { return; } id_t *ids = static_cast( _ecs_vector_first(type, ECS_VECTOR_T(ecs_id_t))); int32_t cur = 0; while (-1 != (cur = ecs_type_index_of(type, cur, pattern))) { flecs::id ent(m_world, ids[cur]); func(ent); cur ++; } } template inline void entity_view::each(const flecs::entity_view& rel, const Func& func) const { return this->match(ecs_pair(rel, flecs::Wildcard), [&](flecs::id id) { flecs::entity obj = id.object(); func(obj); }); } template ::value > > inline bool entity_view::get(const Func& func) const { return _::entity_with_invoker::invoke_get(m_world, m_id, func); } template inline flecs::entity entity_view::get_parent() { return flecs::entity(m_world, ecs_get_parent_w_entity(m_world, m_id, _::cpp_type::id(m_world))); } inline flecs::entity entity_view::get_parent(flecs::entity_view e) { return flecs::entity(m_world, ecs_get_parent_w_entity(m_world, m_id, e.id())); } inline flecs::entity entity_view::lookup(const char *path) const { auto id = ecs_lookup_path_w_sep(m_world, m_id, path, "::", "::", false); return flecs::entity(m_world, id); } } namespace flecs { inline flecs::entity iter::system() const { return flecs::entity(m_iter->world, m_iter->system); } inline flecs::entity iter::self() const { return flecs::entity(m_iter->world, m_iter->self); } inline flecs::world iter::world() const { return flecs::world(m_iter->world); } inline flecs::entity iter::entity(size_t row) const { ecs_assert(row < static_cast(m_iter->count), ECS_COLUMN_INDEX_OUT_OF_RANGE, NULL); if (!this->world().is_readonly()) { return flecs::entity(m_iter->entities[row]) .mut(this->world()); } else { return flecs::entity(this->world().c_ptr(), m_iter->entities[row]); } } /* Obtain column source (0 if self) */ template inline flecs::entity iter_deprecated::column_source(int32_t col) const { return flecs::entity(iter()->world, ecs_term_source(iter(), col)); } /* Obtain component/tag entity of column */ template inline flecs::entity iter_deprecated::column_entity(int32_t col) const { return flecs::entity(iter()->world, ecs_term_id(iter(), col)); } /* Obtain type of column */ template inline type iter_deprecated::column_type(int32_t col) const { return flecs::type(iter()->world, ecs_column_type(iter(), col)); } /* Obtain type of table being iterated over */ template inline type iter_deprecated::table_type() const { return flecs::type(iter()->world, ecs_iter_type(iter())); } template inline column::column(iter &iter, int32_t index) { *this = iter.term(index); } inline flecs::entity iter::term_source(int32_t index) const { return flecs::entity(m_iter->world, ecs_term_source(m_iter, index)); } inline flecs::entity iter::term_id(int32_t index) const { return flecs::entity(m_iter->world, ecs_term_id(m_iter, index)); } /* Obtain type of iter */ inline flecs::type iter::type() const { return flecs::type(m_iter->world, ecs_iter_type(m_iter)); } } // namespace flecs namespace flecs { // emplace for T(flecs::entity, Args...) template , flecs::entity, Args...>::value >> inline void emplace(world_t *world, id_t entity, Args&&... args) { flecs::entity self(world, entity); emplace(world, entity, self, std::forward(args)...); } /** Get id from a type. */ template inline flecs::id world::id() const { return flecs::id(m_world, _::cpp_type::id(m_world)); } template inline flecs::id world::id(Args&&... args) const { return flecs::id(m_world, std::forward(args)...); } template inline flecs::id world::pair() const { return flecs::id( m_world, ecs_pair( _::cpp_type::id(m_world), _::cpp_type::id(m_world))); } template inline flecs::id world::pair(entity_t o) const { return flecs::id( m_world, ecs_pair( _::cpp_type::id(m_world), o)); } inline flecs::id world::pair(entity_t r, entity_t o) const { return flecs::id( m_world, ecs_pair(r, o)); } inline filter_iterator world::begin() const { return filter_iterator(*this, ecs_filter_next); } inline filter_iterator world::end() const { return filter_iterator(ecs_filter_next); } /** All entities created in function are created in scope. All operations * called in function (such as lookup) are relative to scope. */ template void scope(id_t parent, const Func& func); inline void world::init_builtin_components() { pod_component("flecs::core::Component"); pod_component("flecs::core::Type"); pod_component("flecs::core::Identifier"); pod_component("flecs::core::Trigger"); pod_component("flecs::core::Observer"); pod_component("flecs::core::Query"); pod_component("flecs::system::TickSource"); pod_component("flecs::timer::RateFilter"); pod_component("flecs::timer::Timer"); } template inline flecs::entity world::use(const char *alias) { entity_t e = _::cpp_type::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, e); } ecs_use(m_world, e, name); return flecs::entity(m_world, e); } inline flecs::entity world::use(const char *name, const char *alias) { entity_t e = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::", true); ecs_assert(e != 0, ECS_INVALID_PARAMETER, NULL); ecs_use(m_world, e, alias); return flecs::entity(m_world, e); } inline void world::use(flecs::entity e, const char *alias) { entity_t eid = 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, eid); } ecs_use(m_world, eid, alias); } inline flecs::entity world::set_scope(const flecs::entity& s) const { return flecs::entity(ecs_set_scope(m_world, s.id())); } inline flecs::entity world::get_scope() const { return flecs::entity(ecs_get_scope(m_world)); } inline entity world::lookup(const char *name) const { auto e = ecs_lookup_path_w_sep(m_world, 0, name, "::", "::", true); return flecs::entity(*this, e); } template T* world::get_mut() const { flecs::entity e(m_world, _::cpp_type::id(m_world)); return e.get_mut(); } template void world::modified() const { flecs::entity e(m_world, _::cpp_type::id(m_world)); return e.modified(); } template void world::patch(const Func& func) const { flecs::entity e(m_world, _::cpp_type::id(m_world)); e.patch(func); } template const T* world::get() const { flecs::entity e(m_world, _::cpp_type::id(m_world)); return e.get(); } template bool world::has() const { flecs::entity e(m_world, _::cpp_type::id(m_world)); return e.has(); } template void world::add() const { flecs::entity e(m_world, _::cpp_type::id(m_world)); e.add(); } template void world::remove() const { flecs::entity e(m_world, _::cpp_type::id(m_world)); e.remove(); } inline void world::set_pipeline(const flecs::pipeline& pip) const { ecs_set_pipeline(m_world, pip.id()); } template inline flecs::entity world::singleton() { return flecs::entity(m_world, _::cpp_type::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::pipeline world::pipeline(Args &&... args) const { return flecs::pipeline(*this, std::forward(args)...); } inline flecs::system<> world::system(flecs::entity e) const { return flecs::system<>(m_world, e); } template inline flecs::system_builder world::system(Args &&... args) const { return flecs::system_builder(*this, std::forward(args)...); } template inline flecs::observer_builder world::observer(Args &&... args) const { return flecs::observer_builder(*this, std::forward(args)...); } template inline flecs::filter world::filter(Args &&... args) const { return flecs::filter(*this, std::forward(args)...); } template inline flecs::filter_builder world::filter_builder(Args &&... args) const { return flecs::filter_builder(*this, std::forward(args)...); } template inline flecs::query world::query(Args &&... args) const { return flecs::query(*this, std::forward(args)...); } template inline flecs::query_builder world::query_builder(Args &&... args) const { return flecs::query_builder(*this, std::forward(args)...); } template inline flecs::term world::term(Args &&... args) const { return flecs::term(*this, std::forward(args)...); } template inline flecs::term world::term(Args &&... args) const { return flecs::term(*this, std::forward(args)...).id(); } template inline flecs::term world::term(Args &&... args) const { return flecs::term(*this, std::forward(args)...).id(); } 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)...); } template inline void world::each(Func&& func) const { ecs_term_t t = {}; t.id = _::cpp_type::id(); ecs_iter_t it = ecs_term_iter(m_world, &t); while (ecs_term_next(&it)) { _::each_invoker(func).invoke(&it); } } template inline void world::each(flecs::id_t term_id, Func&& func) const { ecs_term_t t = {}; t.id = term_id; ecs_iter_t it = ecs_term_iter(m_world, &t); while (ecs_term_next(&it)) { _::each_invoker(func).invoke(&it); } } namespace _ { // Each with entity parameter template struct filter_invoker_w_ent; template struct filter_invoker_w_ent > { filter_invoker_w_ent(const flecs::world& world, Func&& func) { flecs::filter f(world); f.each(std::move(func)); } }; // Each without entity parameter template struct filter_invoker_no_ent; template struct filter_invoker_no_ent > { filter_invoker_no_ent(const flecs::world& world, Func&& func) { flecs::filter f(world); f.each(std::move(func)); } }; // Switch between function with & without entity parameter template class filter_invoker; template class filter_invoker, flecs::entity>::value> > { public: filter_invoker(const flecs::world& world, Func&& func) { filter_invoker_w_ent>(world, std::move(func)); } }; template class filter_invoker, flecs::entity>::value> > { public: filter_invoker(const flecs::world& world, Func&& func) { filter_invoker_no_ent>(world, std::move(func)); } }; } template inline void world::each(Func&& func) const { _::filter_invoker f_invoker(*this, std::move(func)); } } // namespace flecs namespace flecs { template inline Base& term_builder_i::id(const flecs::type& type) { ecs_assert(m_term != nullptr, ECS_INVALID_PARAMETER, NULL); m_term->pred.entity = type.id(); return *this; } template inline filter_builder_base::operator filter() const { ecs_filter_t filter = *this; return flecs::filter(m_world, &filter); } template inline filter_builder::operator filter<>() const { ecs_filter_t filter = *this; return flecs::filter<>(this->m_world, &filter); } template inline filter filter_builder_base::build() const { ecs_filter_t filter = *this; return flecs::filter(m_world, &filter); } template inline query_builder_base::operator query() const { ecs_query_t *query = *this; return flecs::query(m_world, query); } template inline query_builder::operator query<>() const { ecs_query_t *query = *this; return flecs::query<>(this->m_world, query); } template inline query query_builder_base::build() const { ecs_query_t *query = *this; return flecs::query(m_world, query); } template inline Base& query_builder_i::parent(const query_base& parent) { m_desc->parent = parent.c_ptr(); return *static_cast(this); } template template inline system system_builder::action(Func&& func) const { flecs::entity_t system = build>(std::forward(func), false); return flecs::system(m_world, system); } template template inline system system_builder::iter(Func&& func) const { using Invoker = typename _::iter_invoker< typename std::decay::type, Components...>; flecs::entity_t system = build(std::forward(func), false); return flecs::system(m_world, system); } template template inline system system_builder::each(Func&& func) const { using Invoker = typename _::each_invoker< typename std::decay::type, Components...>; flecs::entity_t system = build(std::forward(func), true); return flecs::system(m_world, system); } template template inline observer observer_builder::iter(Func&& func) const { using Invoker = typename _::iter_invoker< typename std::decay::type, Components...>; flecs::entity_t observer = build(std::forward(func), false); return flecs::observer(m_world, observer); } template template inline observer observer_builder::each(Func&& func) const { using Invoker = typename _::each_invoker< typename std::decay::type, Components...>; flecs::entity_t observer = build(std::forward(func), true); return flecs::observer(m_world, observer); } } #endif #endif #endif #endif