#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#ifndef SIREFLECT_API
#define SIREFLECT_API
#endif
#ifndef SIJSON_API
#define SIJSON_API
#endif
#ifndef SIHTTP_API
#define SIHTTP_API
#endif
#define sireflect_STATIC
#ifndef SIREFLECT_H
#define SIREFLECT_H
#ifndef SIREFLECT_BAKE_CONFIG_H
#define SIREFLECT_BAKE_CONFIG_H
#ifndef sireflect_STATIC
#if defined(sireflect_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__))
#define SIREFLECT_API __declspec(dllexport)
#elif defined(sireflect_EXPORTS)
#define SIREFLECT_API __attribute__((__visibility__("default")))
#elif defined(_MSC_VER)
#define SIREFLECT_API __declspec(dllimport)
#else
#define SIREFLECT_API
#endif
#else
#define SIREFLECT_API
#endif
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) || defined(__clang__)
#define SIREFLECT_UNUSED __attribute__((unused))
#else
#define SIREFLECT_UNUSED
#endif
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
typedef float f32;
typedef double f64;
typedef void *ptr;
typedef struct sireflect_registry_t sireflect_registry_t;
typedef uint64_t sireflect_handle_t;
#define SIREFLECT_INVALID_HANDLE ((sireflect_handle_t)0)
#ifndef NDEBUG
void sireflect_assert_fail(
const char *condition,
const char *message,
const char *file,
int line,
const char *function
);
#define sireflect_assert(condition, message) \
((condition) ? (void)0 \
: sireflect_assert_fail(#condition, message, __FILE__, __LINE__, __func__))
#define sireflect_indebug(...) __VA_ARGS__
#else
#define sireflect_assert(condition, message) ((void)0)
#define sireflect_indebug(...)
#endif
typedef enum {
sireflect_kind_u8,
sireflect_kind_u16,
sireflect_kind_u32,
sireflect_kind_u64,
sireflect_kind_i8,
sireflect_kind_i16,
sireflect_kind_i32,
sireflect_kind_i64,
sireflect_kind_f32,
sireflect_kind_f64,
sireflect_kind_bool,
sireflect_kind_char,
sireflect_kind_short,
sireflect_kind_int,
sireflect_kind_long,
sireflect_kind_ptr,
sireflect_kind_struct,
sireflect_kind_array,
sireflect_kind_pointer,
sireflect_kind_signed_char,
sireflect_kind_unsigned_char,
sireflect_kind_unsigned_short,
sireflect_kind_unsigned_int,
sireflect_kind_unsigned_long,
sireflect_kind_long_long,
sireflect_kind_unsigned_long_long
} sireflect_kind_t;
const char *sireflect_kind_name(sireflect_kind_t kind);
bool sireflect_is_numeric(sireflect_kind_t kind);
typedef enum {
SIREFLECT_QUAL_CONST = 1u << 0,
SIREFLECT_QUAL_VOLATILE = 1u << 1
} sireflect_qualifier_t;
typedef struct {
const char *name;
sireflect_handle_t type;
size_t offset;
size_t size;
size_t align;
uint32_t qualifiers;
} sireflect_field_info_t;
typedef struct {
sireflect_field_info_t *fields;
size_t field_count;
} sireflect_fields_t;
typedef struct {
const char *name;
sireflect_kind_t kind;
size_t size;
size_t align;
sireflect_fields_t fields;
sireflect_handle_t element_type;
size_t element_count;
} sireflect_type_info_t;
typedef struct {
const char *name;
const char *fields;
size_t size;
size_t align;
} sireflect_struct_desc_t;
#define sireflect_desc(type) __##type##_desc
#define SIREFLECT_STRUCT(type_name, ...) \
typedef struct __VA_ARGS__ type_name; \
SIREFLECT_UNUSED static const sireflect_struct_desc_t sireflect_desc(type_name) = { \
.name = #type_name, \
.fields = #__VA_ARGS__, \
.size = sizeof(type_name), \
.align = _Alignof(type_name) \
}
#define sireflect(reg, name) sireflect_register_struct(reg, &sireflect_desc(name))
sireflect_registry_t *sireflect_registry_init(void);
void sireflect_registry_fini(sireflect_registry_t *reg);
const char *sireflect_error(void);
sireflect_handle_t
sireflect_register_struct(sireflect_registry_t *reg, const sireflect_struct_desc_t *desc);
sireflect_handle_t
sireflect_try_register_struct(sireflect_registry_t *reg, const sireflect_struct_desc_t *desc);
sireflect_handle_t sireflect_type_by_name(const sireflect_registry_t *reg, const char *name);
const sireflect_type_info_t *
sireflect_type_info(const sireflect_registry_t *reg, sireflect_handle_t ref);
const sireflect_fields_t *
sireflect_type_fields(const sireflect_registry_t *reg, sireflect_handle_t ref);
size_t sireflect_type_size(const sireflect_registry_t *reg, sireflect_handle_t ref);
const char *sireflect_type_name(const sireflect_registry_t *reg, sireflect_handle_t ref);
bool sireflect_type_is_struct(const sireflect_type_info_t *info);
bool sireflect_type_is_array(const sireflect_type_info_t *info);
bool sireflect_type_is_pointer(const sireflect_type_info_t *info);
sireflect_handle_t sireflect_type_element(const sireflect_registry_t *reg, sireflect_handle_t ref);
size_t sireflect_type_element_count(const sireflect_registry_t *reg, sireflect_handle_t ref);
sireflect_handle_t sireflect_type_pointee(const sireflect_registry_t *reg, sireflect_handle_t ref);
const sireflect_field_info_t *
sireflect_field_info(const sireflect_registry_t *reg, sireflect_handle_t type, const char *field);
sireflect_handle_t
sireflect_field_type(const sireflect_registry_t *reg, sireflect_handle_t type, const char *field);
size_t
sireflect_field_size(const sireflect_registry_t *reg, sireflect_handle_t ref, const char *field);
const void *sireflect_field_ptr(
const sireflect_registry_t *reg,
sireflect_handle_t type,
const void *obj,
const char *field
);
void *sireflect_field_mut_ptr(
const sireflect_registry_t *reg,
sireflect_handle_t type,
void *obj,
const char *field
);
int sireflect_field_copy(
const sireflect_registry_t *reg,
sireflect_handle_t type,
void *obj,
const char *field,
const void *value
);
#ifdef __cplusplus
}
#endif
#endif
#ifndef SIJSON_H
#define SIJSON_H
#include <stdbool.h>
#include <stddef.h>
#define sijson_handle(type) __sijson__##type##__handle
#define SIJSON_DECLARE(type, ...) \
SIREFLECT_STRUCT(type, __VA_ARGS__); \
extern sireflect_handle_t sijson_handle(type);
#define SIJSON_DEFINE(type) sireflect_handle_t sijson_handle(type) = 0;
#define SIJSON(type, ...) \
SIJSON_DECLARE(type, __VA_ARGS__); \
SIJSON_DEFINE(type);
sireflect_registry_t *sijson_default_registry(void);
typedef struct sijson_value *sijson_value_t;
typedef enum sijson_type {
SIJSON_NULL,
SIJSON_BOOL,
SIJSON_NUMBER,
SIJSON_STRING,
SIJSON_ARRAY,
SIJSON_OBJECT,
} sijson_type_t;
sijson_value_t sijson_parse(const char *json);
char *sijson_stringify(sijson_value_t value);
void sijson_clean(void);
void sijson_release(void);
sijson_type_t sijson_type(sijson_value_t value);
bool sijson_bool(sijson_value_t value);
double sijson_number(sijson_value_t value);
const char *sijson_string(sijson_value_t value);
size_t sijson_array_len(sijson_value_t value);
sijson_value_t sijson_array_get(sijson_value_t value, size_t index);
size_t sijson_object_len(sijson_value_t value);
const char *sijson_object_key(sijson_value_t value, size_t index);
sijson_value_t sijson_object_get(sijson_value_t value, const char *key);
sijson_value_t sijson_make_null(void);
sijson_value_t sijson_make_bool(bool value);
sijson_value_t sijson_make_number(double value);
sijson_value_t sijson_make_string(const char *value);
sijson_value_t sijson_make_array(void);
sijson_value_t sijson_make_object(void);
bool sijson_array_push(sijson_value_t array, sijson_value_t value);
bool sijson_object_set(sijson_value_t object, const char *key, sijson_value_t value);
#define sijson_to_json(type, ...) \
sijson_to_json_impl(&sijson_handle(type), &sireflect_desc(type), &(type)__VA_ARGS__)
#define sijson_to_json_ptr(type, ptr) \
sijson_to_json_impl(&sijson_handle(type), &sireflect_desc(type), (ptr))
char *
sijson_to_json_impl(sireflect_handle_t *ref, const sireflect_struct_desc_t *desc, const void *ptr);
#define sijson_from_json(type, json) \
*((type *)sijson_from_json_impl(&sijson_handle(type), &sireflect_desc(type), json))
void *sijson_from_json_impl(
sireflect_handle_t *ref,
const sireflect_struct_desc_t *desc,
const char *json
);
#define sijson_free(type, ptr) sijson_free_impl(&sijson_handle(type), &sireflect_desc(type), (ptr))
void sijson_free_impl(sireflect_handle_t *ref, const sireflect_struct_desc_t *desc, void *ptr);
const char *sijson_error(void);
#endif
#ifndef SIHTTP_H
#define SIHTTP_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) || defined(__clang__)
#define SIHTTP_PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
#else
#define SIHTTP_PRINTF_FORMAT(fmt, args)
#endif
typedef struct sihttp_server_s sihttp_server_t;
typedef struct sihttp_app_state_s sihttp_app_state_t;
typedef enum {
SIHTTP_CONTENT_AUTO = 0,
SIHTTP_CONTENT_TEXT,
SIHTTP_CONTENT_JSON,
SIHTTP_CONTENT_HTML,
SIHTTP_CONTENT_BINARY,
} sihttp_content_type_t;
typedef struct {
int status;
char *body;
sihttp_content_type_t content_type;
} sihttp_response_t;
typedef struct {
const char *method;
const char *path;
const char *body;
sihttp_app_state_t *state;
} sihttp_request_t;
typedef sihttp_response_t (*sihttp_handler_t)(const sihttp_request_t *);
typedef enum {
SIHTTP_METHOD_GET = 0,
SIHTTP_METHOD_POST,
SIHTTP_METHOD_PUT,
SIHTTP_METHOD_DELETE,
SIHTTP_METHOD_OPTIONS,
} sihttp_method_t;
typedef struct {
sihttp_method_t method;
sihttp_handler_t callback;
} sihttp_handler_desc_t;
typedef struct {
int port;
sihttp_app_state_t *state;
int backlog;
int max_requests_per_poll;
} sihttp_server_desc_t;
#define sihttp_server(...) sihttp_server_init(&(sihttp_server_desc_t)__VA_ARGS__)
SIHTTP_API sihttp_server_t *sihttp_server_init(const sihttp_server_desc_t *desc);
SIHTTP_API void sihttp_server_fini(sihttp_server_t *server);
SIHTTP_API int sihttp_server_listen(sihttp_server_t *server, const char *host, uint16_t port);
SIHTTP_API int sihttp_server_start(sihttp_server_t *server);
SIHTTP_API int sihttp_server_poll(sihttp_server_t *server);
SIHTTP_API int sihttp_server_run(sihttp_server_t *server);
SIHTTP_API void sihttp_server_stop(sihttp_server_t *server);
SIHTTP_API uint16_t sihttp_server_port(const sihttp_server_t *server);
#define sihttp_route(server, path, ...) \
sihttp_route_impl(server, path, &(sihttp_handler_desc_t)__VA_ARGS__)
SIHTTP_API void
sihttp_route_impl(sihttp_server_t *server, const char *path, const sihttp_handler_desc_t *desc);
SIHTTP_API void sihttp_get(sihttp_server_t *server, const char *path, sihttp_handler_t callback);
SIHTTP_API void sihttp_post(sihttp_server_t *server, const char *path, sihttp_handler_t callback);
SIHTTP_API void sihttp_put(sihttp_server_t *server, const char *path, sihttp_handler_t callback);
SIHTTP_API void sihttp_delete(sihttp_server_t *server, const char *path, sihttp_handler_t callback);
SIHTTP_API int64_t sihttp_param(const sihttp_request_t *req, const char *name);
SIHTTP_API const char *sihttp_error(void);
SIHTTP_API char *siformat(const char *fmt, ...) SIHTTP_PRINTF_FORMAT(1, 2);
#define sihttp_response(...) ((sihttp_response_t)__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif
#define siecs_STATIC
#ifndef SIECS_H
#define SIECS_H
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#ifndef SIECS_BAKE_CONFIG_H
#define SIECS_BAKE_CONFIG_H
#ifndef siecs_STATIC
#if defined(siecs_EXPORTS) && (defined(_MSC_VER) || defined(__MINGW32__))
#define SIECS_API __declspec(dllexport)
#elif defined(siecs_EXPORTS)
#define SIECS_API __attribute__((__visibility__("default")))
#elif defined(_MSC_VER)
#define SIECS_API __declspec(dllimport)
#else
#define SIECS_API
#endif
#else
#define SIECS_API
#endif
#endif
#ifdef __cplusplus
#ifndef _Alignof
#define _Alignof alignof
#endif
#endif
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define ecs_id(name) _ecs_id_##name##__
struct ecs_world_s;
typedef struct ecs_world_s ecs_world_t;
typedef uint64_t ecs_entity_t;
typedef uint16_t ecs_component_t;
typedef uint16_t ecs_query_id_t;
typedef uint16_t ecs_system_id_t;
typedef uint16_t ecs_event_t;
typedef uint16_t ecs_module_id_t;
typedef uint16_t ecs_resource_t;
typedef uint32_t ecs_observer_id_t;
#define ECS_QUERY_TERM_CAPACITY 64
#define ECS_SYSTEM_AFTER_CAPACITY 16
typedef void (*ecs_module_import_t)(ecs_world_t *world, const void *desc);
typedef struct {
const char *name;
ecs_module_id_t *id;
ecs_module_import_t import;
const void *desc;
uint32_t desc_size;
bool disabled;
} ecs_module_desc_t;
typedef struct {
ecs_world_t *world;
ecs_entity_t entity;
ecs_event_t event;
uintptr_t user_data;
const void *trigger_data;
} ecs_observer_event_t;
typedef void (*ecs_observer_callback_t)(ecs_observer_event_t *event);
typedef void (*ecs_component_on_add_t)(
ecs_world_t *world,
ecs_entity_t entity,
ecs_component_t component,
void *value
);
typedef void (*ecs_component_on_set_t)(
ecs_world_t *world,
ecs_entity_t entity,
ecs_component_t component,
const void *new_value,
void *current_value
);
typedef void (*ecs_component_on_remove_t)(
ecs_world_t *world,
ecs_entity_t entity,
ecs_component_t component,
void *value
);
typedef void (*ecs_type_ctor_t)(void *dst, uint32_t count);
typedef void (*ecs_type_dtor_t)(void *ptr, uint32_t count);
typedef void (*ecs_type_copy_t)(void *dst, const void *src, uint32_t count);
typedef void (*ecs_type_move_t)(void *dst, void *src, uint32_t count);
typedef struct {
ecs_type_ctor_t ctor;
ecs_type_dtor_t dtor;
ecs_type_copy_t copy_ctor;
ecs_type_copy_t copy;
ecs_type_move_t move_ctor;
ecs_type_move_t move;
} ecs_type_ops_t;
typedef enum {
EcsRelationTarget = 1 << 0,
EcsRelationSource = 1 << 1,
EcsRelationCascadeDelete = 1 << 2,
EcsRelationOneToOne = 1 << 3,
EcsRelationOneToMany = 1 << 4
} ecs_relation_flags_t;
typedef struct {
const char *name;
uint64_t size;
ecs_type_ops_t ops;
ecs_component_on_set_t on_set;
ecs_component_on_remove_t on_remove;
ecs_component_on_add_t on_add;
uint32_t relation_flags;
const sireflect_struct_desc_t *struct_desc;
} ecs_component_desc_t;
typedef void (*ecs_resource_hook_t)(ecs_world_t *world, const void *ptr);
typedef struct {
const char *name;
uint64_t size;
ecs_type_ops_t ops;
ecs_resource_hook_t on_set;
ecs_resource_hook_t on_remove;
} ecs_resource_desc_t;
typedef enum {
EcsIn,
EcsOut,
EcsInOut,
EcsInOptional,
EcsInOutOptional,
EcsFilter,
EcsNot,
} ecs_term_access_t;
typedef struct {
ecs_component_t id;
ecs_term_access_t access;
} ecs_query_term_t;
typedef struct {
ecs_query_term_t terms[ECS_QUERY_TERM_CAPACITY];
ecs_entity_t is_a;
} ecs_query_desc_t;
#ifdef __cplusplus
#define ecs_in(cname) \
ecs_query_term_t { ecs_id(cname), EcsIn }
#define ecs_out(cname) \
ecs_query_term_t { ecs_id(cname), EcsOut }
#define ecs_inout(cname) \
ecs_query_term_t { ecs_id(cname), EcsInOut }
#define ecs_in_optional(cname) \
ecs_query_term_t { ecs_id(cname), EcsInOptional }
#define ecs_inout_optional(cname) \
ecs_query_term_t { ecs_id(cname), EcsInOutOptional }
#define ecs_filter(cname) \
ecs_query_term_t { ecs_id(cname), EcsFilter }
#define ecs_not(cname) \
ecs_query_term_t { ecs_id(cname), EcsNot }
#else
#define ecs_in(cname) ((ecs_query_term_t){ ecs_id(cname), EcsIn })
#define ecs_out(cname) ((ecs_query_term_t){ ecs_id(cname), EcsOut })
#define ecs_inout(cname) ((ecs_query_term_t){ ecs_id(cname), EcsInOut })
#define ecs_in_optional(cname) ((ecs_query_term_t){ ecs_id(cname), EcsInOptional })
#define ecs_inout_optional(cname) ((ecs_query_term_t){ ecs_id(cname), EcsInOutOptional })
#define ecs_filter(cname) ((ecs_query_term_t){ ecs_id(cname), EcsFilter })
#define ecs_not(cname) ((ecs_query_term_t){ ecs_id(cname), EcsNot })
#endif
SIECS_API ecs_world_t *ecs_init(void);
typedef struct {
bool rest;
uint16_t target_fps;
} ecs_world_feat_desc_t;
#define ecs_with_features(...) ecs_init_w_features(&(ecs_world_feat_desc_t)__VA_ARGS__)
SIECS_API ecs_world_t *ecs_init_w_features(const ecs_world_feat_desc_t *features);
SIECS_API void ecs_fini(ecs_world_t *world);
SIECS_API void ecs_quit(ecs_world_t *world);
#define ECS_COMPONENT_DECLARE(cname, ...) \
SIJSON_DECLARE(cname, __VA_ARGS__) \
extern ecs_component_t ecs_id(cname); \
extern ecs_component_desc_t ecs_id(cname##_desc)
#define ECS_COMPONENT_DEFINE(cname, ...) \
SIJSON_DEFINE(cname) \
ecs_component_desc_t ecs_id(cname##_desc) = { .name = #cname, \
.size = sizeof(cname), \
.struct_desc = &sireflect_desc(cname), \
__VA_ARGS__ }; \
ecs_component_t ecs_id(cname) = 0
#define ECS_COMPONENT_REGISTER(world, cname) \
ecs_component_register(world, &ecs_id(cname), &ecs_id(cname##_desc))
#define ECS_COMPONENT(cname, ...) \
ECS_COMPONENT_DECLARE(cname, __VA_ARGS__); \
ECS_COMPONENT_DEFINE(cname);
#define ECS_MODULE_DECLARE(module_name, ...) \
typedef struct module_name##_props_t __VA_ARGS__ module_name##_props_t; \
extern ecs_module_id_t ecs_id(module_name); \
void ecs_id(module_name##_import_wrapper)(ecs_world_t * world, const void *desc); \
void module_name##_import(ecs_world_t *world, const module_name##_props_t *props)
#define ECS_MODULE_DEFINE(module_name) \
ecs_module_id_t ecs_id(module_name) = 0; \
void ecs_id(module_name##_import_wrapper)(ecs_world_t * world, const void *desc) { \
module_name##_import(world, (const module_name##_props_t *)desc); \
}
#define ECS_MODULE_IMPORT(world, module_name, ...) \
(ecs_id(module_name) = ecs_module_init( \
world, \
&(ecs_module_desc_t){ \
.name = #module_name, \
.id = &ecs_id(module_name), \
.import = ecs_id(module_name##_import_wrapper), \
.desc = &(module_name##_props_t)__VA_ARGS__, \
.desc_size = sizeof(module_name##_props_t), \
} \
))
#define ecs_module(world, ...) ecs_module_init(world, &(ecs_module_desc_t)__VA_ARGS__)
SIECS_API ecs_module_id_t ecs_module_init(ecs_world_t *world, const ecs_module_desc_t *desc);
SIECS_API ecs_module_id_t ecs_module_find(ecs_world_t *world, const ecs_module_id_t *id);
SIECS_API void ecs_module_enable(ecs_world_t *world, ecs_module_id_t module);
SIECS_API void ecs_module_disable(ecs_world_t *world, ecs_module_id_t module);
SIECS_API bool ecs_module_is_enabled(const ecs_world_t *world, ecs_module_id_t module);
#define ECS_RELATION_DEFINE(cname, flags) \
ecs_component_desc_t ecs_id(cname##_desc) = { \
.name = #cname, \
.size = sizeof(cname), \
.relation_flags = EcsRelationTarget | (flags), \
}; \
ecs_component_t ecs_id(cname) = 0
#define ECS_RELATION(cname, flags) \
ECS_RELATION_DECLARE(cname); \
ECS_RELATION_DEFINE(cname, flags)
#define ecs_source(name) (ecs_id(name) + 1)
#define ECS_RELATION_DECLARE(name) ECS_COMPONENT_DECLARE(name, { ecs_entity_t target; })
ECS_RELATION_DECLARE(ChildOf);
ECS_COMPONENT_DECLARE(Name, { char *value; });
ECS_COMPONENT_DECLARE(Disabled, {});
ECS_COMPONENT_DECLARE(Abstract, {});
#define ecs_component(world, ...) ecs_component_init(world, &(ecs_component_desc_t)__VA_ARGS__)
SIECS_API ecs_component_t ecs_component_init(ecs_world_t *world, const ecs_component_desc_t *desc);
SIECS_API ecs_component_t
ecs_component_register(ecs_world_t *world, ecs_component_t *id, const ecs_component_desc_t *desc);
SIECS_API ecs_entity_t ecs_new(ecs_world_t *world);
SIECS_API void ecs_defer_begin(ecs_world_t *world);
SIECS_API void ecs_defer_end(ecs_world_t *world);
SIECS_API bool ecs_is_deferred(const ecs_world_t *world);
SIECS_API bool ecs_is_alive(const ecs_world_t *world, ecs_entity_t entity);
bool ecs_is(ecs_world_t *world, ecs_entity_t entity, ecs_entity_t target);
SIECS_API void ecs_is_a(ecs_world_t *world, ecs_entity_t entity, ecs_entity_t target);
SIECS_API void ecs_kill(ecs_world_t *world, ecs_entity_t entity);
#define ecs_query(world, ...) ecs_query_init(world, &(ecs_query_desc_t)__VA_ARGS__)
#define ecs_query_each(world, it, i, ...) \
for (ecs_query_id_t _q = ecs_query((world), { { __VA_ARGS__ } }); _q; \
ecs_query_fini((world), _q), _q = 0) \
for (ecs_iter_t it = ecs_query_iter((world), _q); ecs_iter_next(&it);) \
for (uint32_t i = 0; i < it.count; i++)
SIECS_API ecs_query_id_t ecs_query_init(ecs_world_t *world, const ecs_query_desc_t *query);
SIECS_API void ecs_query_fini(ecs_world_t *world, ecs_query_id_t qid);
#define ecs_add(world, entity, cname) ecs_add_cid(world, entity, ecs_id(cname))
SIECS_API void ecs_add_cid(ecs_world_t *world, ecs_entity_t entity, ecs_component_t id);
#define ecs_remove(world, entity, cname) ecs_remove_cid(world, entity, ecs_id(cname))
SIECS_API void ecs_remove_cid(ecs_world_t *world, ecs_entity_t entity, ecs_component_t id);
#define ecs_has(world, entity, cname) ecs_has_cid(world, entity, ecs_id(cname))
SIECS_API bool ecs_has_cid(const ecs_world_t *world, ecs_entity_t entity, ecs_component_t id);
bool ecs_has_cid_owned(const ecs_world_t *world, ecs_entity_t entity, ecs_component_t id);
#define ecs_get(world, entity, cname) ((cname *)ecs_get_cid(world, entity, ecs_id(cname)))
SIECS_API void *ecs_get_cid(ecs_world_t *world, ecs_entity_t entity, ecs_component_t id);
#define ecs_try_get(world, entity, cname) ((cname *)ecs_try_get_cid(world, entity, ecs_id(cname)))
SIECS_API void *ecs_try_get_cid(ecs_world_t *world, ecs_entity_t entity, ecs_component_t cid);
#define ecs_set(world, entity, cname, ...) \
ecs_set_cid(world, entity, ecs_id(cname), &(cname)__VA_ARGS__)
SIECS_API void
ecs_set_cid(ecs_world_t *world, ecs_entity_t entity, ecs_component_t id, const void *data);
SIECS_API void
ecs_move_cid(ecs_world_t *world, ecs_entity_t entity, ecs_component_t id, void *data);
#define ECS_RESOURCE_DECLARE(rname, ...) \
typedef struct rname rname; \
struct rname __VA_ARGS__; \
extern ecs_resource_t ecs_id(rname); \
extern ecs_resource_desc_t ecs_id(rname##_desc)
#define ECS_RESOURCE_DEFINE(rname, ...) \
ecs_resource_desc_t ecs_id(rname##_desc) = { .name = #rname, \
.size = sizeof(rname), \
__VA_ARGS__ }; \
ecs_resource_t ecs_id(rname) = 0
#define ECS_RESOURCE_REGISTER(world, rname) \
ecs_resource_register(world, &ecs_id(rname), &ecs_id(rname##_desc))
#define ECS_RESOURCE(rname, ...) \
ECS_RESOURCE_DECLARE(rname, __VA_ARGS__); \
ECS_RESOURCE_DEFINE(rname)
#define ecs_set_resource(world, rname, ...) \
ecs_set_resource_rid(world, ecs_id(rname), &(rname)__VA_ARGS__)
#define ecs_get_resource(world, rname) ((rname *)ecs_resource_rid(world, ecs_id(rname)))
#define ecs_get_resource_read(world, rname) ((const rname *)ecs_resource_rid(world, ecs_id(rname)))
#define ecs_try_get_resource(world, rname) ((rname *)ecs_try_resource_rid(world, ecs_id(rname)))
#define ecs_try_get_resource_read(world, rname) \
((const rname *)ecs_try_resource_rid(world, ecs_id(rname)))
#define ecs_has_resource(world, rname) ecs_has_resource_rid(world, ecs_id(rname))
#define ecs_remove_resource(world, rname) ecs_remove_resource_rid(world, ecs_id(rname))
#define ecs_resource(world, rname) ecs_get_resource(world, rname)
#define ecs_resource_read(world, rname) ecs_get_resource_read(world, rname)
#define ecs_try_resource(world, rname) ecs_try_get_resource(world, rname)
#define ecs_try_resource_read(world, rname) ecs_try_get_resource_read(world, rname)
SIECS_API ecs_resource_t ecs_resource_init(ecs_world_t *world, const ecs_resource_desc_t *desc);
SIECS_API ecs_resource_t
ecs_resource_register(ecs_world_t *world, ecs_resource_t *id, const ecs_resource_desc_t *desc);
SIECS_API ecs_resource_t ecs_resource_find(ecs_world_t *world, const char *name);
SIECS_API bool ecs_resource_is_registered_rid(const ecs_world_t *world, ecs_resource_t id);
SIECS_API void ecs_set_resource_rid(ecs_world_t *world, ecs_resource_t id, const void *data);
SIECS_API void ecs_move_resource_rid(ecs_world_t *world, ecs_resource_t id, void *data);
SIECS_API void *ecs_resource_rid(ecs_world_t *world, ecs_resource_t id);
SIECS_API void *ecs_try_resource_rid(ecs_world_t *world, ecs_resource_t id);
SIECS_API bool ecs_has_resource_rid(const ecs_world_t *world, ecs_resource_t id);
SIECS_API void ecs_remove_resource_rid(ecs_world_t *world, ecs_resource_t id);
SIECS_API void ecs_with(ecs_world_t *world, ecs_component_t component, ecs_component_t require);
#define EcsOnAdd 0
#define EcsOnRemove 1
#define EcsOnSet 2
typedef struct {
ecs_event_t on;
ecs_query_desc_t query;
ecs_observer_callback_t callback;
uintptr_t user_data;
} ecs_observer_desc_t;
#define ecs_observer(world, ...) ecs_observer_init(world, &(ecs_observer_desc_t)__VA_ARGS__)
SIECS_API ecs_event_t ecs_event(ecs_world_t *world);
SIECS_API ecs_event_t ecs_event_register(ecs_world_t *world, ecs_event_t *id);
SIECS_API ecs_observer_id_t ecs_observer_init(ecs_world_t *world, const ecs_observer_desc_t *desc);
SIECS_API void ecs_observer_enable(ecs_world_t *world, ecs_observer_id_t id);
SIECS_API void ecs_observer_disable(ecs_world_t *world, ecs_observer_id_t id);
SIECS_API void ecs_observer_trigger(
ecs_world_t *world,
ecs_entity_t entity,
ecs_event_t event,
const void *trigger_data
);
typedef enum {
EcsFieldNone,
EcsFieldOwned,
EcsFieldShared,
} ecs_field_kind_t;
typedef struct {
ecs_world_t *world;
uint32_t count;
ecs_entity_t *entities;
void **ptrs;
float delta_time;
struct ecs_query_cache_s *cache;
ecs_field_kind_t *field_kinds;
uintptr_t user_data;
uint16_t table_idx;
uint16_t table_count;
} ecs_iter_t;
SIECS_API ecs_iter_t ecs_query_iter(ecs_world_t *world, ecs_query_id_t query_id);
SIECS_API bool ecs_iter_next(ecs_iter_t *it);
static inline void *ecs_field(ecs_iter_t *it, uint16_t field_index) {
void *field = it->ptrs[field_index];
return it->field_kinds[field_index] == EcsFieldOwned ? *(void **)field : field;
}
static inline bool ecs_field_is_shared(ecs_iter_t *it, uint16_t field_index) {
return it->field_kinds[field_index] == EcsFieldShared;
}
typedef enum {
EcsPreStart,
EcsStart,
EcsPostStart,
EcsOnLoad,
EcsPostLoad,
EcsPreUpdate,
EcsOnUpdate,
EcsPostUpdate,
EcsPreRender,
EcsOnRender,
EcsPostRender,
EcsPhaseCount,
} ecs_phase_t;
#define OnPreUpdate EcsPreUpdate
#define OnUpdate EcsOnUpdate
#define OnPostUpdate EcsPostUpdate
#define OnRender EcsOnRender
typedef struct {
const char *name;
ecs_query_desc_t query;
void (*callback)(ecs_iter_t *);
uintptr_t user_data;
void (*user_data_dtor)(uintptr_t user_data);
ecs_phase_t phase;
ecs_system_id_t after[ECS_SYSTEM_AFTER_CAPACITY];
bool disabled;
} ecs_system_desc_t;
#define ecs_system(world, ...) ecs_system_init(world, &(ecs_system_desc_t)__VA_ARGS__)
SIECS_API ecs_system_id_t ecs_system_init(ecs_world_t *world, const ecs_system_desc_t *desc);
SIECS_API bool ecs_progress(ecs_world_t *world);
SIECS_API void ecs_run_phase(ecs_world_t *world, ecs_phase_t phase);
SIECS_API void ecs_run_system(ecs_world_t *world, ecs_system_id_t system);
SIECS_API void ecs_system_enable(ecs_world_t *world, ecs_system_id_t system);
SIECS_API void ecs_system_disable(ecs_world_t *world, ecs_system_id_t system);
#ifdef __cplusplus
}
#endif
#if defined(__cplusplus) && !defined(SIECS_NO_CPP)
#ifndef SIECS_PUBLIC_CPP_HPP
#define SIECS_PUBLIC_CPP_HPP
#pragma once
#pragma once
#pragma once
#include <string_view>
namespace ecs {
template <class T> consteval std::string_view type_name() {
constexpr std::string_view func = __PRETTY_FUNCTION__;
constexpr std::string_view key = "T = ";
constexpr auto start = func.find(key) + key.size();
constexpr auto end_semi = func.find(';', start);
constexpr auto end_bracket = func.find(']', start);
constexpr auto end = end_semi == std::string_view::npos ? end_bracket : end_semi;
return func.substr(start, end - start);
}
}
#include <cstddef>
#include <cstdio>
#include <memory>
#include <new>
#include <string.h>
#include <string>
#include <type_traits>
#include <utility>
namespace ecs {
namespace detail {
template <typename T> struct component_type {
static inline ecs_component_t id = 0;
};
template <typename T, typename = void> struct is_complete : std::false_type {};
template <typename T> struct is_complete<T, std::void_t<decltype(sizeof(T))>> : std::true_type {};
template <typename T> consteval size_t sisizeof() {
if constexpr (is_complete<T>::value) {
return sizeof(T);
} else {
return 0;
}
}
template <typename T> static void ecs_cpp_set_component_id(ecs_component_t cid) {
detail::component_type<T>::id = cid;
}
template <typename T> static void value_ctor(void *ptr, uint32_t count) {
T *values = static_cast<T *>(ptr);
for (uint32_t i = 0; i < count; i++) {
std::construct_at(&values[i]);
}
}
template <typename T> static void value_dtor(void *ptr, uint32_t count) {
T *values = static_cast<T *>(ptr);
for (uint32_t i = 0; i < count; i++) {
std::destroy_at(&values[i]);
}
}
template <typename T> static void value_copy_ctor(void *dst, const void *src, uint32_t count) {
T *out = static_cast<T *>(dst);
const T *in = static_cast<const T *>(src);
for (uint32_t i = 0; i < count; i++) {
std::construct_at(&out[i], in[i]);
}
}
template <typename T> static void value_copy(void *dst, const void *src, uint32_t count) {
T *out = static_cast<T *>(dst);
const T *in = static_cast<const T *>(src);
for (uint32_t i = 0; i < count; i++) {
if constexpr (std::is_copy_assignable_v<T>) {
out[i] = in[i];
} else {
std::destroy_at(&out[i]);
std::construct_at(&out[i], in[i]);
}
}
}
template <typename T> static void value_move_ctor(void *dst, void *src, uint32_t count) {
T *out = static_cast<T *>(dst);
T *in = static_cast<T *>(src);
for (uint32_t i = 0; i < count; i++) {
std::construct_at(&out[i], std::move(in[i]));
std::destroy_at(&in[i]);
}
}
template <typename T> static void value_move(void *dst, void *src, uint32_t count) {
T *out = static_cast<T *>(dst);
T *in = static_cast<T *>(src);
for (uint32_t i = 0; i < count; i++) {
if constexpr (std::is_move_assignable_v<T>) {
out[i] = std::move(in[i]);
} else {
std::destroy_at(&out[i]);
std::construct_at(&out[i], std::move(in[i]));
}
std::destroy_at(&in[i]);
}
}
template <typename T> consteval ecs_type_ops_t value_ops() {
if constexpr (!is_complete<T>::value) {
return {};
} else if constexpr (sizeof(T) == 0 || std::is_trivially_copyable_v<T>) {
return {};
} else {
return {
.ctor = std::is_default_constructible_v<T> ? value_ctor<T> : nullptr,
.dtor = std::is_destructible_v<T> ? value_dtor<T> : nullptr,
.copy_ctor = std::is_copy_constructible_v<T> ? value_copy_ctor<T> : nullptr,
.copy = std::is_copy_constructible_v<T> ? value_copy<T> : nullptr,
.move_ctor = std::is_move_constructible_v<T> ? value_move_ctor<T> : nullptr,
.move = std::is_move_constructible_v<T> ? value_move<T> : nullptr,
};
}
}
template <typename T> static ecs_component_t ecs_cpp_component_id(ecs_world_t *world) {
ecs_component_t &cid = detail::component_type<T>::id;
if (cid != 0) {
return cid;
}
static sireflect_struct_desc_t reflection = {
.name = strdup(std::string(type_name<T>()).c_str()),
.fields = "{}",
.size = 0,
.align = 1,
};
if constexpr (requires {
{ T::fields } -> std::convertible_to<const char *>;
}) {
reflection.fields = T::fields;
reflection.size = sisizeof<T>();
reflection.align = _Alignof(T);
}
ecs_component_desc_t desc = {
.name = reflection.name,
.size = sisizeof<T>(),
.ops = value_ops<T>(),
.on_set = nullptr,
.on_remove = nullptr,
.on_add = nullptr,
.relation_flags = 0,
.struct_desc = &reflection,
};
cid = ecs_component_init(world, &desc);
return cid;
}
#define fields_str(...) #__VA_ARGS__
#define reflected(...) \
static constexpr const char *fields = fields_str({ __VA_ARGS__ }); \
__VA_ARGS__
}
}
#pragma once
namespace ecs {
class entity {
ecs_entity_t _entity;
ecs_world_t *_world;
public:
static ecs::entity null() { return entity(nullptr, 0); }
entity(ecs_world_t *world, ecs_entity_t entity) : _entity(entity), _world(world) {}
[[nodiscard]] ecs_entity_t id() const noexcept { return _entity; }
template <typename T> entity add() {
ecs_add_cid(_world, _entity, detail::ecs_cpp_component_id<T>(_world));
return *this;
}
operator ecs_entity_t() const noexcept { return _entity; }
entity abstract() {
ecs_add(_world, _entity, Abstract);
return *this;
}
template <typename T> entity remove() {
ecs_remove_cid(_world, _entity, detail::ecs_cpp_component_id<T>(_world));
return *this;
}
template <typename T> [[nodiscard]] bool has() const {
return ecs_has_cid(_world, _entity, detail::ecs_cpp_component_id<T>(_world));
}
template <typename T> entity set(const T &value) {
ecs_set_cid(_world, _entity, detail::ecs_cpp_component_id<T>(_world), &value);
return *this;
}
template <typename T> entity set(T &&value)
requires(!std::is_lvalue_reference_v<T>)
{
using type = std::remove_cvref_t<T>;
ecs_move_cid(_world, _entity, detail::ecs_cpp_component_id<type>(_world), &value);
return *this;
}
entity is_a(entity target) {
ecs_is_a(_world, _entity, target._entity);
return *this;
}
bool is(entity target) { return ecs_is(_world, _entity, target._entity); }
entity child_of(entity parent) {
const ChildOf desc = { .target = parent._entity };
ecs_set_cid(_world, _entity, ecs_id(ChildOf), &desc);
return *this;
}
entity enable() {
ecs_remove_cid(_world, _entity, ecs_id(Disabled));
return *this;
}
entity disable() {
ecs_add_cid(_world, _entity, ecs_id(Disabled));
return *this;
}
[[nodiscard]] bool is_enabled() const {
return !ecs_has_cid(_world, _entity, ecs_id(Disabled));
}
[[nodiscard]] bool is_disabled() const {
return ecs_has_cid(_world, _entity, ecs_id(Disabled));
}
};
}
#pragma once
#include <cassert>
#include <concepts>
namespace ecs {
class world;
template <typename T> class module_ref {
ecs_world_t *_world = nullptr;
ecs_module_id_t _id = 0;
public:
constexpr module_ref() noexcept = default;
constexpr module_ref(ecs_world_t *world, ecs_module_id_t id) noexcept
: _world(world), _id(id) {}
[[nodiscard]] constexpr ecs_module_id_t id() const noexcept { return _id; }
[[nodiscard]] constexpr explicit operator bool() const noexcept { return _id != 0; }
void enable() const noexcept {
assert(_world != nullptr);
assert(_id != 0);
ecs_module_enable(_world, _id);
}
void disable() const noexcept {
assert(_world != nullptr);
assert(_id != 0);
ecs_module_disable(_world, _id);
}
[[nodiscard]] bool is_enabled() const noexcept {
assert(_world != nullptr);
assert(_id != 0);
return ecs_module_is_enabled(_world, _id);
}
};
namespace detail {
template <typename T> struct module_type {
static inline ecs_module_id_t id;
};
template <typename T>
concept module_importable = requires(T module, ecs::world &world) {
{ module.import(world) } -> std::same_as<void>;
};
template <typename T, typename... Args>
concept module_list_initializable =
requires(Args &&...args) { T{ static_cast<Args &&>(args)... }; };
}
}
#pragma once
#pragma once
#include <tuple>
#include <type_traits>
#include <utility>
namespace ecs {
template <typename T> struct function_traits;
template <typename R, typename... Args> struct function_traits<R (*)(Args...)> {
using return_type = R;
using args_tuple = std::tuple<Args...>;
};
template <typename R, typename... Args> struct function_traits<R (&)(Args...)> {
using return_type = R;
using args_tuple = std::tuple<Args...>;
};
template <typename C, typename R, typename... Args>
struct function_traits<R (C::*)(Args...) const> {
using return_type = R;
using args_tuple = std::tuple<Args...>;
};
template <typename C, typename R, typename... Args> struct function_traits<R (C::*)(Args...)> {
using return_type = R;
using args_tuple = std::tuple<Args...>;
};
template <typename F>
struct function_traits : function_traits<decltype(&std::remove_reference_t<F>::operator())> {};
template <typename Tuple, typename Fn, std::size_t... I>
constexpr void for_each_type_impl(Fn &&fn, std::index_sequence<I...>) {
(fn.template operator()<std::tuple_element_t<I, Tuple>>(), ...);
}
template <typename Tuple, typename Fn> constexpr void for_each_type(Fn &&fn) {
constexpr std::size_t N = std::tuple_size_v<Tuple>;
for_each_type_impl<Tuple>(std::forward<Fn>(fn), std::make_index_sequence<N>{});
}
}
#pragma once
#pragma once
#include <cassert>
#include <string>
#include <tuple>
#include <type_traits>
namespace ecs {
template <typename T> class res {
T *_ptr = nullptr;
public:
explicit res(T *ptr) noexcept : _ptr(ptr) { assert(ptr != nullptr); }
[[nodiscard]] T *operator->() const noexcept { return _ptr; }
[[nodiscard]] T &operator*() const noexcept { return *_ptr; }
[[nodiscard]] T *get() const noexcept { return _ptr; }
};
namespace detail {
template <typename T> struct resource_type {
static inline ecs_resource_t id;
};
template <typename T> struct is_res : std::false_type {};
template <typename T> struct is_res<ecs::res<T>> : std::true_type {};
template <typename T> inline constexpr bool is_res_v = is_res<std::remove_cvref_t<T>>::value;
template <typename T> struct res_value;
template <typename T> struct res_value<ecs::res<T>> {
using type = T;
};
template <typename T>
using res_value_t = typename res_value<std::remove_cvref_t<T>>::type;
template <typename T>
using resource_value_t = std::remove_cv_t<res_value_t<T>>;
struct no_resource {};
}
template <typename T> static ecs_resource_t ecs_cpp_resource_id(ecs_world_t *world) {
using type = std::remove_cv_t<T>;
ecs_resource_t &rid = detail::resource_type<type>::id;
if (rid != 0 && ecs_resource_is_registered_rid(world, rid)) {
return rid;
}
std::string name = std::string(type_name<type>());
rid = ecs_resource_find(world, name.c_str());
if (rid != 0) {
return rid;
}
ecs_resource_desc_t desc = {
.name = name.c_str(),
.size = sizeof(type),
.ops = detail::value_ops<type>(),
.on_set = nullptr,
.on_remove = nullptr,
};
rid = ecs_resource_init(world, &desc);
return rid;
}
template <typename T> static ecs_resource_t ecs_cpp_try_resource_id(ecs_world_t *world) {
using type = std::remove_cv_t<T>;
ecs_resource_t &rid = detail::resource_type<type>::id;
if (rid != 0 && ecs_resource_is_registered_rid(world, rid)) {
return rid;
}
std::string name = std::string(type_name<type>());
rid = ecs_resource_find(world, name.c_str());
return rid;
}
namespace detail {
template <typename Arg> inline auto make_resource_arg(ecs_world_t *world) {
if constexpr (is_res_v<Arg>) {
using value_type = res_value_t<Arg>;
using resource_type = std::remove_cv_t<value_type>;
ecs_resource_t id = ecs::ecs_cpp_resource_id<resource_type>(world);
return ecs::res<value_type>(static_cast<value_type *>(ecs_resource_rid(world, id)));
} else {
return no_resource{};
}
}
template <typename Args, std::size_t... Is>
inline auto make_resources(ecs_world_t *world, std::index_sequence<Is...>) {
(void)world;
return std::tuple{ make_resource_arg<std::tuple_element_t<Is, Args>>(world)... };
}
template <typename Args> inline auto make_resources(ecs_world_t *world) {
constexpr std::size_t N = std::tuple_size_v<Args>;
return make_resources<Args>(world, std::make_index_sequence<N>{});
}
}
}
#include <cstdint>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
namespace ecs {
namespace detail {
template <typename T> consteval ecs_term_access_t term_access();
template <typename Args, std::size_t I, std::size_t... Is>
consteval std::size_t field_index_before_impl(std::index_sequence<Is...>) {
return ((Is < I && !is_res_v<std::tuple_element_t<Is, Args>> ? 1U : 0U) + ... + 0U);
}
template <typename Args, std::size_t I> consteval std::size_t field_index_before() {
return field_index_before_impl<Args, I>(std::make_index_sequence<std::tuple_size_v<Args>>{});
}
template <typename Args, std::size_t... Is>
consteval std::size_t component_arg_count_impl(std::index_sequence<Is...>) {
return ((!is_res_v<std::tuple_element_t<Is, Args>> ? 1U : 0U) + ... + 0U);
}
template <typename Args> consteval std::size_t component_arg_count() {
return component_arg_count_impl<Args>(std::make_index_sequence<std::tuple_size_v<Args>>{});
}
template <typename Args, std::size_t I, typename Resources>
inline auto make_batch_arg(ecs_iter_t *it, Resources &resources) {
using arg = std::tuple_element_t<I, Args>;
if constexpr (is_res_v<arg>) {
return std::get<I>(resources);
} else {
using value_type = std::remove_reference_t<arg>;
return static_cast<value_type *>(
ecs_field(it, static_cast<uint16_t>(field_index_before<Args, I>()))
);
}
}
template <typename Args, typename Resources, std::size_t... Is>
inline auto make_fields(ecs_iter_t *it, Resources &resources, std::index_sequence<Is...>) {
(void)it;
return std::tuple{ make_batch_arg<Args, Is>(it, resources)... };
}
template <std::size_t I, typename Args, typename Tuple>
inline decltype(auto) row_arg(Tuple &fields, uint32_t row) {
auto &field = std::get<I>(fields);
using arg = std::tuple_element_t<I, Args>;
if constexpr (is_res_v<arg>) {
(void)row;
return field;
} else {
return field[row];
}
}
template <std::size_t I, typename Args, typename Tuple>
inline decltype(auto) row_arg_shared(Tuple &fields) {
auto &field = std::get<I>(fields);
using arg = std::tuple_element_t<I, Args>;
if constexpr (is_res_v<arg>) {
return field;
} else {
return field[0];
}
}
template <typename F, typename Args, typename Tuple, std::size_t... Is>
inline void call_fields_impl(
F &func,
Tuple &fields,
uint32_t count,
std::uint64_t shared_mask,
std::index_sequence<Is...>
) {
(void)shared_mask;
for (uint32_t i = 0; i < count; i++) {
std::invoke(
func,
((shared_mask & (1ULL << field_index_before<Args, Is>()))
? row_arg_shared<Is, Args>(fields)
: row_arg<Is, Args>(fields, i))...
);
}
}
template <typename F, typename Args, typename Tuple>
inline void call_fields(F &func, Tuple &fields, uint32_t count, std::uint64_t shared_mask) {
constexpr std::size_t N = std::tuple_size_v<std::remove_reference_t<Tuple>>;
constexpr std::size_t component_count = component_arg_count<Args>();
static_assert(component_count <= 64, "query callbacks can read at most 64 components");
call_fields_impl<F, Args>(func, fields, count, shared_mask, std::make_index_sequence<N>{});
}
template <typename Args> inline std::uint64_t make_shared_mask(const ecs_iter_t *it) {
constexpr std::size_t component_count = component_arg_count<Args>();
static_assert(component_count <= 64, "query callbacks can read at most 64 components");
std::uint64_t mask = 0;
for (std::size_t i = 0; i < component_count; i++) {
if (it->field_kinds[i] == EcsFieldShared) {
mask |= 1ULL << i;
}
}
return mask;
}
template <typename T> consteval ecs_term_access_t term_access() {
static_assert(
std::is_lvalue_reference_v<T>,
"query callback arguments must be lvalue references"
);
if constexpr (std::is_const_v<std::remove_reference_t<T>>) {
return EcsIn;
} else {
return EcsInOut;
}
}
inline void append_term(
ecs_query_desc_t &desc,
uint16_t &term_index,
ecs_component_t id,
ecs_term_access_t access
) {
desc.terms[term_index] = {
.id = id,
.access = access,
};
term_index += 1;
}
template <typename... T>
inline void append_terms(
ecs_world_t *world,
ecs_query_desc_t &desc,
uint16_t &term_index,
ecs_term_access_t access
) {
(append_term(desc, term_index, ecs::detail::ecs_cpp_component_id<T>(world), access), ...);
}
template <typename Args>
inline void
append_callback_terms(ecs_world_t *world, ecs_query_desc_t &desc, uint16_t &term_index) {
for_each_type<Args>([&]<typename T>() {
if constexpr (!is_res_v<T>) {
append_term(
desc,
term_index,
ecs::detail::ecs_cpp_component_id<std::remove_cvref_t<T>>(world),
term_access<T>()
);
}
});
}
template <typename F, typename Args>
inline void run_query(F &func, ecs_world_t *world, ecs_query_id_t qid) {
auto resources = make_resources<Args>(world);
ecs_iter_t it = ecs_query_iter(world, qid);
while (ecs_iter_next(&it)) {
auto fields =
make_fields<Args>(&it, resources, std::make_index_sequence<std::tuple_size_v<Args>>{});
call_fields<F, Args>(func, fields, it.count, make_shared_mask<Args>(&it));
}
}
template <typename F, typename Args> inline void run_batch(F &func, ecs_iter_t *it) {
auto resources = make_resources<Args>(it->world);
auto fields =
make_fields<Args>(it, resources, std::make_index_sequence<std::tuple_size_v<Args>>{});
call_fields<F, Args>(func, fields, it->count, make_shared_mask<Args>(it));
}
template <typename F, typename Args> inline void run_once(F &func, ecs_world_t *world) {
static_assert(component_arg_count<Args>() == 0);
auto resources = make_resources<Args>(world);
auto fields =
make_fields<Args>(nullptr, resources, std::make_index_sequence<std::tuple_size_v<Args>>{});
call_fields<F, Args>(func, fields, 1, 0);
}
template <typename F, typename Args>
inline void run_strict_query(F &func, ecs_world_t *world, ecs_query_id_t qid) {
constexpr std::size_t component_count = component_arg_count<Args>();
static_assert(component_count > 0, "query callbacks must read at least one component");
run_query<F, Args>(func, world, qid);
}
}
class world;
class query {
protected:
ecs_query_desc_t desc{};
uint16_t term_index = 0;
ecs_world_t *_world = nullptr;
public:
explicit query(ecs_world_t *world) noexcept : _world(world) {}
template <typename... T> query &&require() {
detail::append_terms<T...>(_world, desc, term_index, EcsFilter);
return std::move(*this);
}
template <typename... T> query &&exclude() {
detail::append_terms<T...>(_world, desc, term_index, EcsNot);
return std::move(*this);
}
query &&is_a(ecs_entity_t target) {
desc.is_a = target;
return std::move(*this);
}
ecs_query_id_t build() { return ecs_query_init(_world, &desc); }
template <typename F> void each(F &&func) {
using traits = function_traits<std::remove_reference_t<F>>;
using args = typename traits::args_tuple;
constexpr std::size_t component_count = detail::component_arg_count<args>();
static_assert(component_count > 0, "query callbacks must read at least one component");
detail::append_callback_terms<args>(_world, desc, term_index);
ecs_query_id_t qid = this->build();
detail::run_strict_query<F, args>(func, _world, qid);
ecs_query_fini(_world, qid);
}
entity first() {
ecs_query_id_t qid = this->build();
ecs_iter_t it = ecs_query_iter(_world, qid);
while (ecs_iter_next(&it)) {
return entity(_world, it.entities[0]);
}
return entity::null();
}
};
}
#include <cstddef>
#include <cstdint>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
namespace ecs {
struct OnAdd {};
struct OnSet {};
struct OnRemove {};
namespace detail {
template <typename T> struct event_type {
static inline ecs_event_t id = UINT16_MAX;
};
template <typename T> static ecs_event_t ecs_cpp_event_id(ecs_world_t *world) {
ecs_event_t &eid = detail::event_type<T>::id;
if (eid != UINT16_MAX) {
return eid;
}
eid = ecs_event(world);
return eid;
}
template <typename T> static void ecs_cpp_set_event_id(ecs_event_t eid) {
detail::event_type<T>::id = eid;
}
template <typename T> decltype(auto) ecs_cpp_observer_arg(ecs_observer_event_t *event) {
using raw = std::remove_cvref_t<T>;
void *ptr = ecs_get_cid(event->world, event->entity, ecs_cpp_component_id<raw>(event->world));
if constexpr (std::is_const_v<std::remove_reference_t<T>>) {
return *static_cast<const raw *>(ptr);
} else {
return *static_cast<raw *>(ptr);
}
}
template <typename Args, std::size_t I, typename Resources>
decltype(auto) ecs_cpp_observer_arg(ecs_observer_event_t *event, Resources &resources) {
using arg = std::tuple_element_t<I, Args>;
if constexpr (is_res_v<arg>) {
(void)event;
return std::get<I>(resources);
} else {
return ecs_cpp_observer_arg<arg>(event);
}
}
template <typename Func, typename Args, std::size_t... Is>
void ecs_cpp_observer_callback_impl(ecs_observer_event_t *event, std::index_sequence<Is...>) {
Func func{};
auto resources = make_resources<Args>(event->world);
std::invoke(func, ecs_cpp_observer_arg<Args, Is>(event, resources)...);
}
template <typename Func, typename Tuple>
void ecs_cpp_observer_callback(ecs_observer_event_t *event) {
ecs_cpp_observer_callback_impl<Func, Tuple>(
event,
std::make_index_sequence<std::tuple_size_v<Tuple>>{}
);
}
}
template <typename T> class observer : public query {
public:
observer(ecs_world_t *world) : query(world) {};
template <typename F> ecs_observer_id_t each(F &&) {
using callback = std::remove_cvref_t<F>;
static_assert(
std::is_empty_v<callback> && std::is_default_constructible_v<callback>,
"observer callbacks must be stateless with the current C API"
);
using traits = function_traits<callback>;
using args = typename traits::args_tuple;
static_assert(
detail::component_arg_count<args>() > 0,
"observer callbacks must read at least one component"
);
ecs::detail::append_callback_terms<args>(_world, this->desc, term_index);
ecs_observer_desc_t observer_desc = {
.on = detail::ecs_cpp_event_id<T>(_world),
.query = this->desc,
.callback = detail::ecs_cpp_observer_callback<callback, args>,
};
return ecs_observer_init(_world, &observer_desc);
}
};
}
#pragma once
namespace ecs {
namespace detail {
template <typename Callback, typename Args> static void system_callback(ecs_iter_t *it) {
Callback &callback = *reinterpret_cast<Callback *>(it->user_data);
if constexpr (component_arg_count<Args>() == 0) {
run_once<Callback, Args>(callback, it->world);
} else {
run_batch<Callback, Args>(callback, it);
}
}
template <typename Callback> static void system_callback_dtor(uintptr_t user_data) {
delete reinterpret_cast<Callback *>(user_data);
}
}
class system : protected query {
const char *name;
ecs_phase_t _phase = EcsOnUpdate;
public:
system(ecs_world_t *_world, const char *name) : query(_world), name(name) {}
template <typename... T> system require() {
query::require<T...>();
return *this;
}
template <typename... T> system exclude() {
query::exclude<T...>();
return *this;
}
system phase(ecs_phase_t _phase) {
this->_phase = _phase;
return *this;
}
template <typename F> ecs_system_id_t each(F &&func) {
using callback = std::remove_cvref_t<F>;
using traits = function_traits<callback>;
using args = typename traits::args_tuple;
detail::append_callback_terms<args>(_world, desc, term_index);
callback *state = new callback(std::forward<F>(func));
ecs_system_desc_t system_desc = {
.name = name,
.query = this->desc,
.callback = detail::system_callback<callback, args>,
.user_data = reinterpret_cast<uintptr_t>(state),
.user_data_dtor = detail::system_callback_dtor<callback>,
.phase = _phase,
};
return ecs_system_init(_world, &system_desc);
}
};
}
#include <cstring>
namespace ecs {
enum class world_ownership : uint8_t {
owned,
borrowed,
};
class world {
ecs_world_t *_world = nullptr;
world_ownership _ownership = world_ownership::owned;
template <typename T> static void import_module_callback(ecs_world_t *raw, const void *ptr) {
ecs::world world = ecs::world::borrow(raw);
T &module = *static_cast<T *>(const_cast<void *>(ptr));
module.import(world);
}
template <typename T> [[nodiscard]] module_ref<T> import_module(T &module) const {
static const std::string name = std::string(type_name<T>());
ecs_module_desc_t desc = {
.name = name.c_str(),
.id = &detail::module_type<T>::id,
.import = import_module_callback<T>,
.desc = &module,
.desc_size = sizeof(T),
.disabled = false,
};
return module_ref<T>(_world, ecs_module_init(_world, &desc));
}
public:
world() noexcept : _world(nullptr), _ownership(world_ownership::owned) {
ecs_world_feat_desc_t desc{ .rest = true, .target_fps = 120 };
_world = ecs_init_w_features(&desc);
detail::ecs_cpp_set_component_id<Disabled>(ecs_id(Disabled));
detail::ecs_cpp_set_component_id<Name>(ecs_id(Name));
detail::ecs_cpp_set_component_id<ChildOf>(ecs_id(ChildOf));
detail::ecs_cpp_set_event_id<OnAdd>(EcsOnAdd);
detail::ecs_cpp_set_event_id<OnAdd>(EcsOnSet);
detail::ecs_cpp_set_event_id<OnAdd>(EcsOnRemove);
}
explicit world(ecs_world_t *world) noexcept
: _world(world), _ownership(world_ownership::owned) {}
world(ecs_world_t *world, world_ownership ownership) noexcept
: _world(world), _ownership(ownership) {}
[[nodiscard]] static world borrow(ecs_world_t *world) noexcept {
return ecs::world(world, world_ownership::borrowed);
}
world(const world &) = delete;
world &operator=(const world &) = delete;
world(world &&other) noexcept
: _world(std::exchange(other._world, nullptr)),
_ownership(std::exchange(other._ownership, world_ownership::owned)) {}
world &operator=(world &&other) noexcept {
if (this != &other) {
reset();
_world = std::exchange(other._world, nullptr);
_ownership = std::exchange(other._ownership, world_ownership::owned);
}
return *this;
}
~world() noexcept { reset(); }
[[nodiscard]] ecs_world_t *c_ptr() const noexcept { return _world; }
operator ecs_world_t *() const noexcept { return _world; }
void reset(
ecs_world_t *world = nullptr,
world_ownership ownership = world_ownership::owned
) noexcept {
if (_world != nullptr && _ownership == world_ownership::owned) {
ecs_fini(_world);
}
_world = world;
_ownership = ownership;
}
template <typename T> ecs_component_t component() const {
return detail::ecs_cpp_component_id<T>(_world);
}
template <typename T> void set_resource(T &&value) const {
using type = std::remove_cvref_t<T>;
if constexpr (std::is_lvalue_reference_v<T>) {
ecs_set_resource_rid(_world, ecs_cpp_resource_id<type>(_world), &value);
} else {
ecs_move_resource_rid(_world, ecs_cpp_resource_id<type>(_world), &value);
}
}
template <typename T> [[nodiscard]] T &resource() const {
using type = std::remove_cv_t<T>;
return *static_cast<T *>(ecs_resource_rid(_world, ecs_cpp_resource_id<type>(_world)));
}
template <typename T> [[nodiscard]] T *try_resource() const {
using type = std::remove_cv_t<T>;
ecs_resource_t id = ecs_cpp_try_resource_id<type>(_world);
return id ? static_cast<T *>(ecs_try_resource_rid(_world, id)) : nullptr;
}
template <typename T> [[nodiscard]] bool has_resource() const {
using type = std::remove_cv_t<T>;
ecs_resource_t id = ecs_cpp_try_resource_id<type>(_world);
return id && ecs_has_resource_rid(_world, id);
}
template <typename T> void remove_resource() const {
using type = std::remove_cv_t<T>;
ecs_resource_t id = ecs_cpp_try_resource_id<type>(_world);
if (id) {
ecs_remove_resource_rid(_world, id);
}
}
template <typename T>
requires detail::module_importable<T>
module_ref<T> import(T module) const {
return import_module<T>(module);
}
template <typename T, typename... Args>
requires detail::module_importable<T> && detail::module_list_initializable<T, Args...>
module_ref<T> import(Args &&...args) const {
T module{ std::forward<Args>(args)... };
return import_module<T>(module);
}
template <typename T> [[nodiscard]] module_ref<T> module() const noexcept {
return module_ref<T>(_world, ecs_module_find(_world, &detail::module_type<T>::id));
}
template <typename T> observer<T> observe() const { return observer<T>(_world); }
template <typename T> [[nodiscard]] ecs_event_t event() const {
return detail::ecs_cpp_event_id<T>(_world);
}
template <typename T> void trigger(ecs::entity entity, const void *data = nullptr) const {
ecs_observer_trigger(_world, entity.id(), event<T>(), data);
}
[[nodiscard]] ecs::entity entity(const char *name = nullptr) const {
auto e = ecs::entity(_world, ecs_new(_world));
if (name) {
e.set<Name>({ .value = strdup(name) });
}
return e;
}
[[nodiscard]] ecs::entity instantiate(ecs::entity e) { return this->entity().is_a(e); }
[[nodiscard]] ecs::query query() const { return ecs::query(_world); }
[[nodiscard]] ecs::system system(const char *name = "unamed") const {
return ecs::system(_world, name);
}
bool progress() { return ecs_progress(_world); }
};
}
#endif
#endif
#endif