diff --git a/code/foundation/src/gui/spritesheet_viewer.c b/code/foundation/src/gui/spritesheet_viewer.c new file mode 100644 index 0000000..318952d --- /dev/null +++ b/code/foundation/src/gui/spritesheet_viewer.c @@ -0,0 +1,14 @@ +void spritesheet_viewer(struct nk_context *ctx, struct nk_image spritesheet, Vector2 frameSize, int framesPerRow) { + const int maxFrames = (int)((spritesheet.w*spritesheet.h) / (frameSize.x*frameSize.y)); + nk_layout_row_static(ctx, 32, 32, (int)(nk_window_get_size(ctx).x / frameSize.x) -1); + for(int frame = 0; frame < maxFrames; frame++) { + float ox = (frame % framesPerRow) * frameSize.x; + float oy = (int)(frame / framesPerRow) * frameSize.y; + spritesheet.region[0] = (nk_ushort)ox; + spritesheet.region[1] = (nk_ushort)oy; + spritesheet.region[2] = (nk_ushort)frameSize.x; + spritesheet.region[3] = (nk_ushort)frameSize.y; + nk_image(ctx, spritesheet); + nk_labelf(ctx, NK_TEXT_ALIGN_LEFT, "%d", frame); + } +} diff --git a/code/foundation/src/systems/modules/system_phys.c b/code/foundation/src/systems/modules/system_phys.c new file mode 100644 index 0000000..8de2275 --- /dev/null +++ b/code/foundation/src/systems/modules/system_phys.c @@ -0,0 +1,80 @@ + +void PhysOnCreateBody(ecs_iter_t *it) { + PhysicsBody *pb = ecs_field(it, PhysicsBody, 1); + Position *p = ecs_field(it, Position, 2); + + for (int i = 0; i < it->count; i++) { + if (pb[i].body_ptr > 0) continue; + const frMaterial mat = { + .density = pb[i].density, + .staticFriction = pb[i].static_friction, + .dynamicFriction = pb[i].dynamic_friction, + }; + + frShape *shape = 0; + if (pb[i].kind == PHYS_CIRCLE) { + shape = frCreateCircle(mat, pb[i].circle.r); + } else { + shape = frCreateRectangle(mat, pb[i].rect.w, pb[i].rect.h); + } + + frBodyFlags flags = 0x0; + if (pb[i].inf_inertia) flags |= FR_FLAG_INFINITE_INERTIA; + if (pb[i].inf_mass) flags |= FR_FLAG_INFINITE_MASS; + frBody *body = frCreateBodyFromShape(FR_BODY_DYNAMIC, flags, frVec2PixelsToMeters((Vector2){p[i].x, p[i].y}), shape); + frAddToWorld(phys_world, body); + pb[i].body_ptr = (uintptr_t)body; + } +} + +void PhysOnRemoveBody(ecs_iter_t *it) { + PhysicsBody *pb = ecs_field(it, PhysicsBody, 1); + + for (int i = 0; i < it->count; i++) { + frBody *body = (frBody*)pb[i].body_ptr; + frRemoveFromWorld(phys_world, body); + frShape *shape = frGetBodyShape(body); + frReleaseBody(body); + frReleaseShape(shape); + } +} + +void PhysSetVelocity(ecs_iter_t *it) { + PhysicsBody *pb = ecs_field(it, PhysicsBody, 1); + Velocity *v = ecs_field(it, Velocity, 2); + + for (int i = 0; i < it->count; i++) { + frBody *body = (frBody*)pb[i].body_ptr; + frSetBodyVelocity(body, frVec2PixelsToMeters((Vector2) { v[i].x , v[i].y })); + } +} + +void PhysUpdatePosition(ecs_iter_t *it) { + PhysicsBody *pb = ecs_field(it, PhysicsBody, 1); + Position *p = ecs_field(it, Position, 2); + Velocity *v = ecs_field(it, Velocity, 3); + + for (int i = 0; i < it->count; i++) { + frBody *body = (frBody*)pb[i].body_ptr; + Vector2 pos = frVec2MetersToPixels(frGetBodyPosition(body)); + entity_set_position(it->entities[i], pos.x, pos.y); + Vector2 vel = frVec2MetersToPixels(frGetBodyVelocity(body)); + v[i].x = vel.x; + v[i].y = vel.y; + } +} + +void PhysResetPosition(ecs_iter_t *it) { + Position *p = ecs_field(it, Position, 1); + + for (int i = 0; i < it->count; i++) { + const PhysicsBody *pb = ecs_get(it->world, it->entities[i], PhysicsBody); + if (!pb) continue; + frBody *body = (frBody*)pb->body_ptr; + frSetBodyPosition(body, frVec2PixelsToMeters((Vector2){p[i].x, p[i].y})); + } +} + +void PhysSimulateWorld(ecs_iter_t *it) { + frSimulateWorld(phys_world, 1.0f/60.0f); +} diff --git a/code/foundation/src/utils/raylib_helpers.h b/code/foundation/src/utils/raylib_helpers.h index c41a85e..d85d3c7 100644 --- a/code/foundation/src/utils/raylib_helpers.h +++ b/code/foundation/src/utils/raylib_helpers.h @@ -252,3 +252,48 @@ void DrawRectangleLinesEco(float posX, float posY, float width, float height, Co rlVertex2f(posX + 1, posY + 1); rlEnd(); } + +//------------------------------------------------------------------------ +// SPRITESHEET +//------------------------------------------------------------------------ + +extern float platform_frametime(); + +typedef struct { + Texture2D texture; + Vector2 frameSize; + int framesPerRow; + Vector2 origin; +} SpriteSheet; + +typedef struct { + SpriteSheet *spritesheet; + int start; + int numFrames; + float tickDelay; // in seconds + + int frame; // output + + // transient data + float nextTickTime; +} SpriteAnimation; + +static inline +void DrawSpriteEco(SpriteSheet* sprite, int frame, float x, float y, float ang, float scale, Color c) { + float ox = (frame % sprite->framesPerRow) * sprite->frameSize.x; + float oy = (int)(frame / sprite->framesPerRow) * sprite->frameSize.y; + DrawTexturePro(sprite->texture, (Rectangle){ox, oy, sprite->frameSize.x,sprite->frameSize.y}, + (Rectangle){x, y, sprite->frameSize.x * scale, sprite->frameSize.y * scale}, + (Vector2){sprite->origin.x * scale, sprite->origin.y * scale}, ang, c); +} + +static inline +int TickSpriteAnimation(SpriteAnimation *anim) { + if (anim->nextTickTime < platform_frametime()) { + anim->nextTickTime = platform_frametime() + anim->tickDelay; + + anim->frame = anim->start + (anim->frame + 1) % anim->numFrames; + } + + return anim->frame; +} diff --git a/code/games/survival/src/platform.c b/code/games/survival/src/platform.c index 3d04322..e0384e2 100644 --- a/code/games/survival/src/platform.c +++ b/code/games/survival/src/platform.c @@ -27,6 +27,7 @@ ZPL_DIAGNOSTIC_POP #include "gui/ui_skin.c" #include "gui/tooltip.c" #include "gui/notifications.c" +#include "gui/spritesheet_viewer.c" #include "renderer.c" @@ -91,48 +92,6 @@ void platform_input() { } } -void debug_draw_spritesheet() { - if (nk_begin(game_ui, "Spritesheet debug", nk_rect(660, 100, 240, 800), - NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|NK_WINDOW_TITLE)) - { - nk_layout_row_dynamic(game_ui, 0, 2); - - static int spritesheet_frame_id = 0; - if(nk_button_label(game_ui, "Prev")){ - spritesheet_frame_id-=100; - } - - if(nk_button_label(game_ui, "Next")){ - spritesheet_frame_id+=100; - } - - static bool loaded = false; - static struct nk_image nuclear_image; - static int max_frames = 0; - if (!loaded) { - nuclear_image = TextureToNuklear(main_sprite_sheet.texture); - max_frames = nuclear_image.w*nuclear_image.h / (32*32); - loaded = true; - } - - nk_layout_row_static(game_ui, 32, 32, (int)(nk_window_get_size(game_ui).x / 32.f) -1); - for(int i = 0; i < 100; i++) { - int frame = spritesheet_frame_id + i; - frame %= max_frames; - float ox = (frame % main_sprite_sheet.framesWide) * main_sprite_sheet.frameSize.x; - float oy = (int)(frame / main_sprite_sheet.framesWide) * main_sprite_sheet.frameSize.y; - nuclear_image.region[0] = (nk_short)ox; - nuclear_image.region[1] = (nk_short)oy; - nuclear_image.region[2] = 32; - nuclear_image.region[3] = 32; - nk_image(game_ui, nuclear_image); - nk_labelf(game_ui, NK_TEXT_ALIGN_LEFT, "%d", frame); - } - - nk_end(game_ui); - } -} - void platform_render() { platform_resize_window(); @@ -162,7 +121,14 @@ void platform_render() { renderer_debug_draw(); debug_draw(); - debug_draw_spritesheet(); + + if (nk_begin(game_ui, "Spritesheet Viewer", nk_rect(460, 100, 800, 600), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|NK_WINDOW_TITLE)) + { + spritesheet_viewer(game_ui, main_sprite_sheet_nk, main_sprite_sheet.frameSize, main_sprite_sheet.framesPerRow); + nk_end(game_ui); + } + notification_draw(); game_draw_ui(); } diff --git a/code/games/survival/src/renderer.c b/code/games/survival/src/renderer.c index 08490d7..49fbe1b 100644 --- a/code/games/survival/src/renderer.c +++ b/code/games/survival/src/renderer.c @@ -1,9 +1,8 @@ -#include "spritesheet.c" - static Camera2D render_camera; static float zoom_overlay_tran = 0.0f; static SpriteSheet main_sprite_sheet = { 0 }; +static struct nk_image main_sprite_sheet_nk = { 0 }; #define CAM_OVERLAY_ZOOM_LEVEL 0.15f #define ALPHA(x) ColorAlpha(x, data->tran_time) @@ -95,7 +94,7 @@ void renderer_draw_entry(uint64_t key, entity_view *data, game_world_render_entr DrawNametag("Player", key, data, x, y-16); //DrawTextureRec(GetSpriteTexture2D(assets_find(ASSET_PLAYER)), ASSET_SRC_RECT(), (Vector2){data->x-(WORLD_BLOCK_SIZE/2), data->y-(WORLD_BLOCK_SIZE/2)}, ColorAlpha(WHITE, data->tran_time)); //DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time)); - sprite_draw(&main_sprite_sheet, 129, x, y, 0.0f, 2.0f, WHITE); + DrawSpriteEco(&main_sprite_sheet, 129, x, y, 0.0f, 2.0f, WHITE); //if (data->has_items && !data->inside_vehicle) { // float ix = data->x; @@ -167,8 +166,10 @@ void renderer_init(void) { // NOTE(DavoSK): Init others spritesheets here main_sprite_sheet.texture = LoadTexture("art/gen/spritesheet.png"); main_sprite_sheet.frameSize = (Vector2){ 32, 32 }; - main_sprite_sheet.framesWide = 64; + main_sprite_sheet.framesPerRow = 64; main_sprite_sheet.origin = (Vector2){ 16, 16 }; + + main_sprite_sheet_nk = TextureToNuklear(main_sprite_sheet.texture); } void renderer_shutdown(void) { diff --git a/code/games/survival/src/spritesheet.c b/code/games/survival/src/spritesheet.c deleted file mode 100644 index 59f11cc..0000000 --- a/code/games/survival/src/spritesheet.c +++ /dev/null @@ -1,18 +0,0 @@ -typedef struct { - Texture2D texture; - Vector2 frameSize; - int framesWide; - Vector2 origin; -} SpriteSheet; - -// enum { -// SPRITE_PLAYER = -// }; - -void sprite_draw(SpriteSheet* sprite, int frame, float x, float y, float ang, float scale, Color c) { - float ox = (frame % sprite->framesWide) * sprite->frameSize.x; - float oy = (int)(frame / sprite->framesWide) * sprite->frameSize.y; - DrawTexturePro(sprite->texture, (Rectangle){ox, oy, sprite->frameSize.x,sprite->frameSize.y}, - (Rectangle){x, y, sprite->frameSize.x * scale, sprite->frameSize.y * scale}, - (Vector2){sprite->origin.x * scale, sprite->origin.y * scale}, ang, c); -} \ No newline at end of file diff --git a/code/vendors/flecs/flecs.c b/code/vendors/flecs/flecs.c index 38b503f..4b35492 100644 --- a/code/vendors/flecs/flecs.c +++ b/code/vendors/flecs/flecs.c @@ -627,6 +627,7 @@ typedef struct ecs_query_table_t { ecs_table_cache_hdr_t hdr; /* Header for ecs_table_cache_t */ ecs_query_table_match_t *first; /* List with matches for table */ ecs_query_table_match_t *last; /* Last discovered match for table */ + uint64_t table_id; int32_t rematch_count; /* Track whether table was rematched */ } ecs_query_table_t; @@ -767,6 +768,7 @@ typedef enum ecs_cmd_kind_t { EcsOpEmplace, EcsOpMut, EcsOpModified, + EcsOpAddModified, EcsOpDelete, EcsOpClear, EcsOpOnDeleteAction, @@ -997,7 +999,7 @@ void ecs_table_cache_replace( void* ecs_table_cache_remove( ecs_table_cache_t *cache, - const ecs_table_t *table, + uint64_t table_id, ecs_table_cache_hdr_t *elem); void* ecs_table_cache_get( @@ -1242,9 +1244,6 @@ void flecs_emit( bool flecs_default_observer_next_callback( ecs_iter_t *it); -void flecs_default_uni_observer_run_callback( - ecs_iter_t *it); - void flecs_observers_invoke( ecs_world_t *world, ecs_map_t *observers, @@ -1796,10 +1795,6 @@ void flecs_notify_tables( ecs_id_t id, ecs_table_event_t *event); -void flecs_notify_queries( - ecs_world_t *world, - ecs_query_event_t *event); - void flecs_register_table( ecs_world_t *world, ecs_table_t *table); @@ -2213,6 +2208,13 @@ void flecs_instantiate( int32_t row, int32_t count); +void* flecs_get_base_component( + const ecs_world_t *world, + ecs_table_t *table, + ecs_id_t id, + ecs_id_record_t *table_index, + int32_t recur_depth); + //////////////////////////////////////////////////////////////////////////////// //// Query API //////////////////////////////////////////////////////////////////////////////// @@ -3055,6 +3057,7 @@ void flecs_table_records_unregister( ecs_world_t *world, ecs_table_t *table) { + uint64_t table_id = table->id; int32_t i, count = table->record_count; for (i = 0; i < count; i ++) { ecs_table_record_t *tr = &table->records[i]; @@ -3067,7 +3070,7 @@ void flecs_table_records_unregister( ECS_INTERNAL_ERROR, NULL); (void)id; - ecs_table_cache_remove(cache, table, &tr->hdr); + ecs_table_cache_remove(cache, table_id, &tr->hdr); flecs_id_record_release(world, (ecs_id_record_t*)cache); } @@ -3469,12 +3472,14 @@ void flecs_table_free( ecs_assert(table->refcount == 0, ECS_INTERNAL_ERROR, NULL); - if (!is_root) { - flecs_notify_queries( - world, &(ecs_query_event_t){ - .kind = EcsQueryTableUnmatch, - .table = table - }); + if (!is_root && !(world->flags & EcsWorldQuit)) { + flecs_emit(world, world, &(ecs_event_desc_t) { + .ids = &table->type, + .event = EcsOnTableDelete, + .table = table, + .flags = EcsEventTableOnly, + .observable = world + }); } if (ecs_should_log_2()) { @@ -5420,7 +5425,6 @@ void* flecs_get_component( return flecs_get_component_ptr(world, table, row, id).ptr; } -static void* flecs_get_base_component( const ecs_world_t *world, ecs_table_t *table, @@ -6141,12 +6145,12 @@ const ecs_entity_t* flecs_bulk_new( ecs_data_t *data = &table->data; int32_t row = flecs_table_appendn(world, table, data, count, entities); - + /* Update entity index. */ int i; ecs_record_t **records = ecs_vec_first(&data->records); for (i = 0; i < count; i ++) { - ecs_record_t *r = flecs_entities_ensure(world, entities[i]); + ecs_record_t *r = flecs_entities_get(world, entities[i]); r->table = table; r->row = ECS_ROW_TO_RECORD(row + i, 0); records[row + i] = r; @@ -6157,9 +6161,6 @@ const ecs_entity_t* flecs_bulk_new( (component_data == NULL) ? 0 : EcsEventNoOnSet); if (component_data) { - /* Set components that we're setting in the component mask so the init - * actions won't call OnSet triggers for them. This ensures we won't - * call OnSet triggers multiple times for the same component */ int32_t c_i; ecs_table_t *storage_table = table->storage_table; for (c_i = 0; c_i < component_ids->count; c_i ++) { @@ -6293,29 +6294,60 @@ flecs_component_ptr_t flecs_get_mut( if (r->table) { dst = flecs_get_component_ptr( world, r->table, ECS_RECORD_TO_ROW(r->row), id); + if (dst.ptr) { + return dst; + } } - if (!dst.ptr) { - /* If entity didn't have component yet, add it */ - flecs_add_id_w_record(world, entity, r, id, true); + /* If entity didn't have component yet, add it */ + flecs_add_id_w_record(world, entity, r, id, true); - /* Flush commands so the pointer we're fetching is stable */ - flecs_defer_end(world, &world->stages[0]); - flecs_defer_begin(world, &world->stages[0]); - - ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(r->table->storage_table != NULL, ECS_INTERNAL_ERROR, NULL); - dst = flecs_get_component_ptr( - world, r->table, ECS_RECORD_TO_ROW(r->row), id); - - return dst; - } + /* Flush commands so the pointer we're fetching is stable */ + flecs_defer_end(world, &world->stages[0]); + flecs_defer_begin(world, &world->stages[0]); + ecs_assert(r->table != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(r->table->storage_table != NULL, ECS_INTERNAL_ERROR, NULL); + dst = flecs_get_component_ptr( + world, r->table, ECS_RECORD_TO_ROW(r->row), id); error: return dst; } /* -- Private functions -- */ +static +void flecs_invoke_hook( + ecs_world_t *world, + ecs_table_t *table, + int32_t count, + ecs_entity_t *entities, + void *ptr, + ecs_id_t id, + const ecs_type_info_t *ti, + ecs_entity_t event, + ecs_iter_action_t hook) +{ + ecs_assert(ti->size != 0, ECS_INVALID_PARAMETER, NULL); + + ecs_iter_t it = { .field_count = 1}; + it.entities = entities; + + flecs_iter_init(world, &it, flecs_iter_cache_all); + it.world = world; + it.real_world = world; + it.table = table; + it.ptrs[0] = ptr; + it.sizes[0] = ti->size; + it.ids[0] = id; + it.event = event; + it.event_id = id; + it.ctx = ti->hooks.ctx; + it.binding_ctx = ti->hooks.binding_ctx; + it.count = count; + flecs_iter_validate(&it); + hook(&it); + ecs_iter_fini(&it); +} void flecs_notify_on_set( ecs_world_t *world, @@ -6354,28 +6386,9 @@ void flecs_notify_on_set( ecs_iter_action_t on_set = ti->hooks.on_set; if (on_set) { ecs_vec_t *c = &table->data.columns[column]; - ecs_size_t size = ti->size; - void *ptr = ecs_vec_get(c, size, row); - ecs_assert(size != 0, ECS_INTERNAL_ERROR, NULL); - - ecs_iter_t it = { .field_count = 1}; - it.entities = entities; - - flecs_iter_init(world, &it, flecs_iter_cache_all); - it.world = world; - it.real_world = world; - it.table = table; - it.ptrs[0] = ptr; - it.sizes[0] = size; - it.ids[0] = id; - it.event = EcsOnSet; - it.event_id = id; - it.ctx = ti->hooks.ctx; - it.binding_ctx = ti->hooks.binding_ctx; - it.count = count; - flecs_iter_validate(&it); - on_set(&it); - ecs_iter_fini(&it); + void *ptr = ecs_vec_get(c, ti->size, row); + flecs_invoke_hook(world, table, count, entities, ptr, id, + ti, EcsOnSet, on_set); } } } @@ -6544,6 +6557,8 @@ ecs_entity_t ecs_new_low_id( flecs_entities_ensure(world, id); } + ecs_assert(ecs_get_type(world, id) == NULL, ECS_INTERNAL_ERROR, NULL); + return id; error: return 0; @@ -7089,9 +7104,10 @@ const ecs_entity_t* ecs_bulk_init( ecs_type_t ids; ecs_table_t *table = desc->table; - ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; - flecs_table_diff_builder_init(world, &diff); if (!table) { + ecs_table_diff_builder_t diff = ECS_TABLE_DIFF_INIT; + flecs_table_diff_builder_init(world, &diff); + int32_t i = 0; ecs_id_t id; while ((id = desc->ids[i])) { @@ -7101,17 +7117,21 @@ const ecs_entity_t* ecs_bulk_init( ids.array = (ecs_id_t*)desc->ids; ids.count = i; - } else { - diff.added.array = table->type.array; - diff.added.count = table->type.count; - ids = (ecs_type_t){.array = diff.added.array, .count = diff.added.count}; - } - ecs_table_diff_t table_diff; - flecs_table_diff_build_noalloc(&diff, &table_diff); - flecs_bulk_new(world, table, entities, &ids, count, desc->data, true, NULL, - &table_diff); - flecs_table_diff_builder_fini(world, &diff); + ecs_table_diff_t table_diff; + flecs_table_diff_build_noalloc(&diff, &table_diff); + flecs_bulk_new(world, table, entities, &ids, count, desc->data, true, NULL, + &table_diff); + flecs_table_diff_builder_fini(world, &diff); + } else { + ecs_table_diff_t diff = { + .added.array = table->type.array, + .added.count = table->type.count + }; + ids = (ecs_type_t){.array = diff.added.array, .count = diff.added.count}; + flecs_bulk_new(world, table, entities, &ids, count, desc->data, true, NULL, + &diff); + } if (!sparse_count) { return entities; @@ -7757,15 +7777,15 @@ void ecs_delete( ecs_flags32_t row_flags = ECS_RECORD_TO_ROW_FLAGS(r->row); ecs_table_t *table; if (row_flags) { - if (row_flags & EcsEntityObservedId) { - flecs_on_delete(world, entity, 0, true); - flecs_on_delete(world, ecs_pair(entity, EcsWildcard), 0, true); - } if (row_flags & EcsEntityObservedTarget) { flecs_on_delete(world, ecs_pair(EcsFlag, entity), 0, true); flecs_on_delete(world, ecs_pair(EcsWildcard, entity), 0, true); r->idr = NULL; } + if (row_flags & EcsEntityObservedId) { + flecs_on_delete(world, entity, 0, true); + flecs_on_delete(world, ecs_pair(entity, EcsWildcard), 0, true); + } if (row_flags & EcsEntityObservedAcyclic) { table = r->table; if (table) { @@ -7944,9 +7964,10 @@ void* ecs_get_mut_id( } ecs_record_t *r = flecs_entities_get(world, entity); + ecs_assert(r != NULL, ECS_INTERNAL_ERROR, NULL); void *result = flecs_get_mut(world, entity, id, r).ptr; ecs_check(result != NULL, ECS_INVALID_PARAMETER, NULL); - + flecs_defer_end(world, stage); return result; error: @@ -8690,7 +8711,6 @@ error: return 0; } - ecs_entity_t ecs_set_name( ecs_world_t *world, ecs_entity_t entity, @@ -8830,9 +8850,17 @@ void ecs_ensure( ecs_world_t *world, ecs_entity_t entity) { - ecs_poly_assert(world, ecs_world_t); /* Cannot be a stage */ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); ecs_check(entity != 0, ECS_INVALID_PARAMETER, NULL); + /* Const cast is safe, function checks for threading */ + world = (ecs_world_t*)ecs_get_world(world); + + /* The entity index can be mutated while in staged/readonly mode, as long as + * the world is not multithreaded. */ + ecs_assert(!(world->flags & EcsWorldMultiThreaded), + ECS_INVALID_OPERATION, NULL); + /* Check if a version of the provided id is alive */ ecs_entity_t any = ecs_get_alive(world, ecs_strip_generation(entity)); if (any == entity) { @@ -9386,6 +9414,7 @@ void flecs_cmd_batch_for_entity( world->info.cmd.batched_entity_count ++; + ecs_table_t *start_table = table; ecs_cmd_t *cmd; int32_t next_for_entity; ecs_table_diff_t table_diff; /* Keep track of diff for observers/hooks */ @@ -9423,10 +9452,39 @@ void flecs_cmd_batch_for_entity( ecs_cmd_kind_t kind = cmd->kind; switch(kind) { + case EcsOpAddModified: + /* Add is batched, but keep Modified */ + cmd->kind = EcsOpModified; + kind = EcsOpAdd; + + /* fallthrough */ case EcsOpAdd: table = flecs_find_table_add(world, table, id, diff); world->info.cmd.batched_command_count ++; break; + case EcsOpModified: { + if (start_table) { + /* If a modified was inserted for an existing component, the value + * of the component could have been changed. If this is the case, + * call on_set hooks before the OnAdd/OnRemove observers are invoked + * when moving the entity to a different table. + * This ensures that if OnAdd/OnRemove observers access the modified + * component value, the on_set hook has had the opportunity to + * run first to set any computed values of the component. */ + int32_t row = ECS_RECORD_TO_ROW(r->row); + flecs_component_ptr_t ptr = flecs_get_component_ptr( + world, start_table, row, cmd->id); + if (ptr.ptr) { + ecs_type_info_t *ti = ptr.ti; + ecs_iter_action_t on_set; + if ((on_set = ti->hooks.on_set)) { + flecs_invoke_hook(world, start_table, 1, &entity, + ptr.ptr, cmd->id, ptr.ti, EcsOnSet, on_set); + } + } + } + break; + } case EcsOpSet: case EcsOpMut: table = flecs_find_table_add(world, table, id, diff); @@ -9613,6 +9671,12 @@ bool flecs_defer_end( flecs_modified_id_if(world, e, id); world->info.cmd.modified_count ++; break; + case EcsOpAddModified: + flecs_add_id(world, e, id); + flecs_modified_id_if(world, e, id); + world->info.cmd.add_count ++; + world->info.cmd.modified_count ++; + break; case EcsOpDelete: { ecs_delete(world, e); world->info.cmd.delete_count ++; @@ -9873,7 +9937,7 @@ bool flecs_defer_modified( ecs_id_t id) { if (flecs_defer_cmd(stage)) { - ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, false); + ecs_cmd_t *cmd = flecs_cmd_new(stage, entity, false, true); if (cmd) { cmd->kind = EcsOpModified; cmd->id = id; @@ -10048,7 +10112,7 @@ void* flecs_defer_set( if (!cmd) { if (need_value) { /* Entity is deleted by a previous command, but we still need to - * return a temporary storage to the application. */ + * return a temporary storage to the application. */ cmd_kind = EcsOpSkip; } else { /* No value needs to be returned, we can drop the command */ @@ -10056,24 +10120,25 @@ void* flecs_defer_set( } } + /* Find type info for id */ const ecs_type_info_t *ti = NULL; ecs_id_record_t *idr = flecs_id_record_get(world, id); if (!idr) { /* If idr doesn't exist yet, create it but only if the - * application is not multithreaded. */ + * application is not multithreaded. */ if (stage->async || (world->flags & EcsWorldMultiThreaded)) { ti = ecs_get_type_info(world, id); ecs_assert(ti != NULL, ECS_INVALID_PARAMETER, NULL); } else { /* When not in multi threaded mode, it's safe to find or - * create the id record. */ + * create the id record. */ idr = flecs_id_record_ensure(world, id); ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL); /* Get type_info from id record. We could have called - * ecs_get_type_info directly, but since this function can be - * expensive for pairs, creating the id record ensures we can - * find the type_info quickly for subsequent operations. */ + * ecs_get_type_info directly, but since this function can be + * expensive for pairs, creating the id record ensures we can + * find the type_info quickly for subsequent operations. */ ti = idr->type_info; } } else { @@ -10087,61 +10152,127 @@ void* flecs_defer_set( ecs_assert(!size || size == ti->size, ECS_INVALID_PARAMETER, NULL); size = ti->size; - ecs_stack_t *stack = &stage->defer_stack; - void *op_value = flecs_stack_alloc(stack, size, ti->alignment); - - bool emplace = cmd_kind == EcsOpEmplace; - if (!value && !emplace) { - /* Const cast is safe, value will only be moved when this is an - * emplace op */ - value = (void*)ecs_get_id(world, entity, id); - } - - if (value) { - if (emplace) { - ecs_move_t move; - if ((move = ti->hooks.move_ctor)) { - move(op_value, value, 1, ti); - } else { - ecs_os_memcpy(op_value, value, size); - } - } else { - ecs_copy_t copy; - if ((copy = ti->hooks.copy_ctor)) { - copy(op_value, value, 1, ti); - } else { - ecs_os_memcpy(op_value, value, size); + /* Find existing component. Make sure it's owned, so that we won't use the + * component of a prefab. */ + void *existing = NULL; + ecs_table_t *table = NULL, *storage_table; + if (idr) { + /* Entity can only have existing component if id record exists */ + ecs_record_t *r = flecs_entities_get(world, entity); + table = r->table; + if (r && table && (storage_table = table->storage_table)) { + const ecs_table_record_t *tr = flecs_id_record_get_table( + idr, storage_table); + if (tr) { + /* Entity has the component */ + ecs_vec_t *column = &table->data.columns[tr->column]; + existing = ecs_vec_get(column, size, ECS_RECORD_TO_ROW(r->row)); } } - } else if (!emplace) { - ecs_xtor_t ctor; - if ((ctor = ti->hooks.ctor)) { - ctor(op_value, 1, ti); + } + + /* Get existing value from storage */ + void *cmd_value = existing; + bool emplace = cmd_kind == EcsOpEmplace; + + /* If the component does not yet exist, create a temporary value. This is + * necessary so we can store a component value in the deferred command, + * without adding the component to the entity which is not allowed in + * deferred mode. */ + if (!existing) { + ecs_stack_t *stack = &stage->defer_stack; + cmd_value = flecs_stack_alloc(stack, size, ti->alignment); + + /* If the component doesn't yet exist, construct it and move the + * provided value into the component, if provided. Don't construct if + * this is an emplace operation, in which case the application is + * responsible for constructing. */ + if (value) { + if (emplace) { + ecs_move_t move = ti->hooks.move_ctor; + if (move) { + move(cmd_value, value, 1, ti); + } else { + ecs_os_memcpy(cmd_value, value, size); + } + } else { + ecs_copy_t copy = ti->hooks.copy_ctor; + if (copy) { + copy(cmd_value, value, 1, ti); + } else { + ecs_os_memcpy(cmd_value, value, size); + } + } + } else if (!emplace) { + /* If the command is not an emplace, construct the temp storage */ + + /* Check if entity inherits component */ + void *base = NULL; + if (table && (table->flags & EcsTableHasIsA)) { + base = flecs_get_base_component(world, table, id, idr, 0); + } + + if (!base) { + /* Normal ctor */ + ecs_xtor_t ctor = ti->hooks.ctor; + if (ctor) { + ctor(cmd_value, 1, ti); + } + } else { + /* Override */ + ecs_copy_t copy = ti->hooks.copy_ctor; + if (copy) { + copy(cmd_value, base, 1, ti); + } else { + ecs_os_memcpy(cmd_value, base, size); + } + } + } + } else if (value) { + /* If component exists and value is provided, copy */ + ecs_copy_t copy = ti->hooks.copy; + if (copy) { + copy(existing, value, 1, ti); + } else { + ecs_os_memcpy(existing, value, size); } } if (!cmd) { - /* If op is NULL, entity was already deleted. Check if we need to - * insert an operation into the queue */ + /* If cmd is NULL, entity was already deleted. Check if we need to + * insert a command into the queue. */ if (!ti->hooks.dtor) { /* If temporary memory does not need to be destructed, it'll get - * freed when the stack allocator is reset. This prevents us - * from having to insert an operation when the entity was - * already deleted. */ - return op_value; + * freed when the stack allocator is reset. This prevents us + * from having to insert a command when the entity was + * already deleted. */ + return cmd_value; } cmd = flecs_cmd_alloc(stage); } - cmd->kind = cmd_kind; - cmd->id = id; - cmd->idr = idr; - cmd->entity = entity; - cmd->is._1.size = size; - cmd->is._1.value = op_value; - - return op_value; + if (!existing) { + /* If component didn't exist yet, insert command that will create it */ + cmd->kind = cmd_kind; + cmd->id = id; + cmd->idr = idr; + cmd->entity = entity; + cmd->is._1.size = size; + cmd->is._1.value = cmd_value; + } else { + /* If component already exists, still insert an Add command to ensure + * that any preceding remove commands won't remove the component. If the + * operation is a set, also insert a Modified command. */ + if (cmd_kind == EcsOpSet) { + cmd->kind = EcsOpAddModified; + } else { + cmd->kind = EcsOpAdd; + } + cmd->id = id; + cmd->entity = entity; + } + return cmd_value; error: return NULL; } @@ -11537,6 +11668,7 @@ void* flecs_sparse_ensure( } else if (dense > count) { /* If dense is not alive, swap it with the first unused element. */ flecs_sparse_swap_dense(sparse, page, dense, count); + dense = count; /* First unused element is now last used element */ sparse->count ++; @@ -25566,6 +25698,28 @@ ecs_entity_t ecs_struct_init( return t; } +ecs_entity_t ecs_opaque_init( + ecs_world_t *world, + const ecs_opaque_desc_t *desc) +{ + ecs_poly_assert(world, ecs_world_t); + ecs_assert(desc != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(desc->as_type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(desc->serialize != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_entity_t t = desc->entity; + if (!t) { + t = ecs_new_low_id(world); + } + + ecs_set(world, t, EcsOpaque, { + .as_type = desc->as_type, + .serialize = desc->serialize + }); + + return t; +} + ecs_entity_t ecs_unit_init( ecs_world_t *world, const ecs_unit_desc_t *desc) @@ -25796,6 +25950,23 @@ ecs_vector_t* serialize_vector( return ops; } +static +ecs_vector_t* serialize_custom_type( + ecs_world_t *world, + ecs_entity_t type, + ecs_size_t offset, + ecs_vector_t *ops) +{ + (void)world; + + ecs_meta_type_op_t *op = ops_add(&ops, EcsOpOpaque); + op->offset = offset; + op->type = type; + op->size = type_size(world, type); + + return ops; +} + static ecs_vector_t* serialize_struct( ecs_world_t *world, @@ -25891,6 +26062,10 @@ ecs_vector_t* serialize_type( case EcsVectorType: ops = serialize_vector(world, type, offset, ops); break; + + case EcsOpaqueType: + ops = serialize_custom_type(world, type, offset, ops); + break; } return ops; @@ -26766,6 +26941,34 @@ void flecs_set_vector(ecs_iter_t *it) { } } +static +void flecs_set_custom_type(ecs_iter_t *it) { + ecs_world_t *world = it->world; + EcsOpaque *serialize = ecs_field(it, EcsOpaque, 1); + + int i, count = it->count; + for (i = 0; i < count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_entity_t elem_type = serialize[i].as_type; + + if (!elem_type) { + ecs_err("custom type '%s' has no mapping type", ecs_get_name(world, e)); + continue; + } + + const EcsComponent *comp = ecs_get(world, e, EcsComponent); + if (!comp || !comp->size || !comp->alignment) { + ecs_err("custom type '%s' has no size/alignment, register as component first", + ecs_get_name(world, e)); + continue; + } + + if (flecs_init_type(world, e, EcsOpaqueType, comp->size, comp->alignment)) { + continue; + } + } +} + bool flecs_unit_validate( ecs_world_t *world, ecs_entity_t t, @@ -26963,6 +27166,7 @@ void FlecsMetaImport( flecs_bootstrap_component(world, EcsStruct); flecs_bootstrap_component(world, EcsArray); flecs_bootstrap_component(world, EcsVector); + flecs_bootstrap_component(world, EcsOpaque); flecs_bootstrap_component(world, EcsUnit); flecs_bootstrap_component(world, EcsUnitPrefix); @@ -27069,6 +27273,12 @@ void FlecsMetaImport( .callback = flecs_set_vector }); + ecs_observer_init(world, &(ecs_observer_desc_t){ + .filter.terms[0] = { .id = ecs_id(EcsOpaque), .src.flags = EcsSelf }, + .events = {EcsOnSet}, + .callback = flecs_set_custom_type + }); + ecs_observer_init(world, &(ecs_observer_desc_t){ .filter.terms[0] = { .id = ecs_id(EcsUnit), .src.flags = EcsSelf }, .events = {EcsOnSet}, @@ -27160,7 +27370,8 @@ void FlecsMetaImport( {.name = "EnumType"}, {.name = "StructType"}, {.name = "ArrayType"}, - {.name = "VectorType"} + {.name = "VectorType"}, + {.name = "OpaqueType"} } }); @@ -27226,6 +27437,14 @@ void FlecsMetaImport( } }); + ecs_struct_init(world, &(ecs_struct_desc_t){ + .entity = ecs_id(EcsOpaque), + .members = { + { .name = (char*)"as_type", .type = ecs_id(ecs_entity_t) }, + { .name = (char*)"serialize", .type = ecs_id(ecs_uptr_t) } + } + }); + ecs_entity_t ut = ecs_struct_init(world, &(ecs_struct_desc_t){ .entity = ecs_entity(world, { .name = "unit_translation" }), .members = { @@ -33584,6 +33803,91 @@ int json_ser_vector( return json_ser_type_elements(world, v->type, array, count, str); } +typedef struct json_serializer_ctx_t { + ecs_strbuf_t *str; + bool is_collection; + bool is_struct; +} json_serializer_ctx_t; + +static +int json_ser_custom_value( + const ecs_meta_serializer_t *ser, + ecs_entity_t type, + const void *value) +{ + json_serializer_ctx_t *json_ser = ser->ctx; + if (json_ser->is_collection) { + ecs_strbuf_list_next(json_ser->str); + } + return ecs_ptr_to_json_buf(ser->world, type, value, json_ser->str); +} + +static +int json_ser_custom_member( + const ecs_meta_serializer_t *ser, + const char *name) +{ + json_serializer_ctx_t *json_ser = ser->ctx; + if (!json_ser->is_struct) { + ecs_err("serializer::member can only be called for structs"); + return -1; + } + flecs_json_member(json_ser->str, name); + return 0; +} + +static +int json_ser_custom_type( + const ecs_world_t *world, + ecs_meta_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const EcsOpaque *ct = ecs_get(world, op->type, EcsOpaque); + ecs_assert(ct != NULL, ECS_INVALID_OPERATION, NULL); + ecs_assert(ct->as_type != 0, ECS_INVALID_OPERATION, NULL); + + const EcsMetaType *pt = ecs_get(world, ct->as_type, EcsMetaType); + ecs_assert(pt != NULL, ECS_INVALID_OPERATION, NULL); + + ecs_type_kind_t kind = pt->kind; + bool is_collection = false; + bool is_struct = false; + + if (kind == EcsStructType) { + flecs_json_object_push(str); + is_struct = true; + } else if (kind == EcsArrayType || kind == EcsVectorType) { + flecs_json_array_push(str); + is_collection = true; + } + + json_serializer_ctx_t json_ser = { + .str = str, + .is_struct = is_struct, + .is_collection = is_collection + }; + + ecs_meta_serializer_t ser = { + .world = world, + .value = json_ser_custom_value, + .member = json_ser_custom_member, + .ctx = &json_ser + }; + + if (ct->serialize(&ser, base)) { + return -1; + } + + if (kind == EcsStructType) { + flecs_json_object_pop(str); + } else if (kind == EcsArrayType || kind == EcsVectorType) { + flecs_json_array_pop(str); + } + + return 0; +} + /* Forward serialization to the different type kinds */ static int json_ser_type_op( @@ -33626,6 +33930,11 @@ int json_ser_type_op( goto error; } break; + case EcsOpOpaque: + if (json_ser_custom_type(world, op, ECS_OFFSET(ptr, op->offset), str)) { + goto error; + } + break; case EcsOpEntity: { ecs_entity_t e = *(ecs_entity_t*)ECS_OFFSET(ptr, op->offset); if (!e) { @@ -33814,7 +34123,7 @@ char* ecs_ptr_to_json( } static -bool skip_id( +bool flecs_json_skip_id( const ecs_world_t *world, ecs_id_t id, const ecs_entity_to_json_desc_t *desc, @@ -33903,7 +34212,7 @@ int flecs_json_append_type_labels( int32_t i; for (i = 0; i < count; i ++) { ecs_entity_t pred = 0, obj = 0, role = 0; - if (skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { + if (flecs_json_skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { continue; } @@ -33960,7 +34269,7 @@ int flecs_json_append_type_values( bool hidden; ecs_entity_t pred = 0, obj = 0, role = 0; ecs_id_t id = ids[i]; - if (skip_id(world, id, desc, ent, inst, &pred, &obj, &role, + if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, &hidden)) { continue; @@ -34023,7 +34332,7 @@ int flecs_json_append_type_info( bool hidden; ecs_entity_t pred = 0, obj = 0, role = 0; ecs_id_t id = ids[i]; - if (skip_id(world, id, desc, ent, inst, &pred, &obj, &role, + if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, &hidden)) { continue; @@ -34079,7 +34388,7 @@ int flecs_json_append_type_hidden( bool hidden; ecs_entity_t pred = 0, obj = 0, role = 0; ecs_id_t id = ids[i]; - if (skip_id(world, id, desc, ent, inst, &pred, &obj, &role, + if (flecs_json_skip_id(world, id, desc, ent, inst, &pred, &obj, &role, &hidden)) { continue; @@ -34117,7 +34426,7 @@ int flecs_json_append_type( for (i = 0; i < count; i ++) { ecs_entity_t pred = 0, obj = 0, role = 0; - if (skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { + if (flecs_json_skip_id(world, ids[i], desc, ent, inst, &pred, &obj, &role, 0)) { continue; } @@ -34429,7 +34738,29 @@ void flecs_json_serialize_iter_result_ids( for (int i = 0; i < it->field_count; i ++) { flecs_json_next(buf); - flecs_json_serialize_id(world, ecs_field_id(it, i + 1), buf); + flecs_json_serialize_id(world, ecs_field_id(it, i + 1), buf); + } + + flecs_json_array_pop(buf); +} + +static +void flecs_json_serialize_iter_result_table_type( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + if (!it->table) { + return; + } + + flecs_json_memberl(buf, "ids"); + flecs_json_array_push(buf); + + ecs_type_t *type = &it->table->type; + for (int i = 0; i < type->count; i ++) { + flecs_json_next(buf); + flecs_json_serialize_id(world, type->array[i], buf); } flecs_json_array_pop(buf); @@ -34736,6 +35067,63 @@ void flecs_json_serialize_iter_result_values( flecs_json_array_pop(buf); } +static +void flecs_json_serialize_iter_result_columns( + const ecs_world_t *world, + const ecs_iter_t *it, + ecs_strbuf_t *buf) +{ + ecs_table_t *table = it->table; + if (!table || !table->storage_table) { + return; + } + + flecs_json_memberl(buf, "values"); + flecs_json_array_push(buf); + + ecs_type_t *type = &table->type; + int32_t *storage_map = table->storage_map; + ecs_assert(storage_map != NULL, ECS_INTERNAL_ERROR, NULL); + + for (int i = 0; i < type->count; i ++) { + int32_t storage_column = -1; + if (storage_map) { + storage_column = storage_map[i]; + } + + ecs_strbuf_list_next(buf); + + if (storage_column == -1) { + ecs_strbuf_appendch(buf, '0'); + continue; + } + + ecs_entity_t typeid = table->type_info[storage_column]->component; + if (!typeid) { + ecs_strbuf_appendch(buf, '0'); + continue; + } + + const EcsComponent *comp = ecs_get(world, typeid, EcsComponent); + if (!comp) { + ecs_strbuf_appendch(buf, '0'); + continue; + } + + const EcsMetaTypeSerialized *ser = ecs_get( + world, typeid, EcsMetaTypeSerialized); + if (!ser) { + ecs_strbuf_appendch(buf, '0'); + continue; + } + + void *ptr = ecs_vec_first(&table->data.columns[storage_column]); + array_to_json_buf_w_type_data(world, ptr, it->count, buf, comp, ser); + } + + flecs_json_array_pop(buf); +} + static void flecs_json_serialize_iter_result( const ecs_world_t *world, @@ -34748,12 +35136,16 @@ void flecs_json_serialize_iter_result( /* Each result can be matched with different component ids. Add them to * the result so clients know with which component an entity was matched */ - if (!desc || desc->serialize_ids) { - flecs_json_serialize_iter_result_ids(world, it, buf); + if (desc && desc->serialize_table) { + flecs_json_serialize_iter_result_table_type(world, it, buf); + } else { + if (!desc || desc->serialize_ids) { + flecs_json_serialize_iter_result_ids(world, it, buf); + } } /* Include information on which entity the term is matched with */ - if (!desc || desc->serialize_ids) { + if (!desc || (desc->serialize_sources && !desc->serialize_table)) { flecs_json_serialize_iter_result_sources(world, it, buf); } @@ -34773,12 +35165,12 @@ void flecs_json_serialize_iter_result( } /* Include information on which terms are set, to support optional terms */ - if (!desc || desc->serialize_is_set) { + if (!desc || (desc->serialize_is_set && !desc->serialize_table)) { flecs_json_serialize_iter_result_is_set(it, buf); } /* Write entity ids for current result (for queries with This terms) */ - if (!desc || desc->serialize_entities) { + if (!desc || desc->serialize_entities || desc->serialize_table) { flecs_json_serialize_iter_result_entities(world, it, buf); } @@ -34798,8 +35190,12 @@ void flecs_json_serialize_iter_result( } /* Serialize component values */ - if (!desc || desc->serialize_values) { - flecs_json_serialize_iter_result_values(world, it, buf); + if (desc && desc->serialize_table) { + flecs_json_serialize_iter_result_columns(world, it, buf); + } else { + if (!desc || desc->serialize_values) { + flecs_json_serialize_iter_result_values(world, it, buf); + } } flecs_json_object_pop(buf); @@ -34838,6 +35234,12 @@ int ecs_iter_to_json_buf( /* Use instancing for improved performance */ ECS_BIT_SET(it->flags, EcsIterIsInstanced); + /* If serializing entire table, don't bother letting the iterator populate + * data fields as we'll be iterating all columns. */ + if (desc && desc->serialize_table) { + ECS_BIT_SET(it->flags, EcsIterIsFilter); + } + ecs_iter_next_action_t next = it->next; while (next(it)) { flecs_json_serialize_iter_result(world, it, buf, desc); @@ -34871,6 +35273,34 @@ char* ecs_iter_to_json( return ecs_strbuf_get(&buf); } +int ecs_world_to_json_buf( + ecs_world_t *world, + ecs_strbuf_t *buf_out) +{ + ecs_filter_t f = ECS_FILTER_INIT; + ecs_filter(world, { + .terms = {{ .id = EcsAny }}, + .storage = &f + }); + + ecs_iter_t it = ecs_filter_iter(world, &f); + ecs_iter_to_json_desc_t json_desc = { .serialize_table = true }; + return ecs_iter_to_json_buf(world, &it, buf_out, &json_desc); +} + +char* ecs_world_to_json( + ecs_world_t *world) +{ + ecs_strbuf_t buf = ECS_STRBUF_INIT; + + if (ecs_world_to_json_buf(world, &buf)) { + ecs_strbuf_reset(&buf); + return NULL; + } + + return ecs_strbuf_get(&buf); +} + #endif /** @@ -35059,6 +35489,13 @@ int json_typeinfo_ser_type_op( ecs_meta_type_op_t *op, ecs_strbuf_t *str) { + if (op->kind == EcsOpOpaque) { + const EcsOpaque *ct = ecs_get(world, op->type, + EcsOpaque); + ecs_assert(ct != NULL, ECS_INTERNAL_ERROR, NULL); + return json_typeinfo_ser_type(world, ct->as_type, str); + } + flecs_json_array_push(str); switch(op->kind) { @@ -35079,6 +35516,10 @@ int json_typeinfo_ser_type_op( case EcsOpVector: json_typeinfo_ser_vector(world, op->type, str); break; + case EcsOpOpaque: + /* Can't happen, already handled above */ + ecs_abort(ECS_INTERNAL_ERROR, NULL); + break; default: if (json_typeinfo_ser_primitive( flecs_json_op_to_primitive_kind(op->kind), str)) @@ -35122,15 +35563,15 @@ int json_typeinfo_ser_type_ops( if (op->name) { flecs_json_member(str, op->name); } + } - int32_t elem_count = op->count; - if (elem_count > 1 && op != ops) { - flecs_json_array_push(str); - json_typeinfo_ser_array(world, op->type, op->count, str); - flecs_json_array_pop(str); - i += op->op_count - 1; - continue; - } + int32_t elem_count = op->count; + if (elem_count > 1) { + flecs_json_array_push(str); + json_typeinfo_ser_array(world, op->type, op->count, str); + flecs_json_array_pop(str); + i += op->op_count - 1; + continue; } switch(op->kind) { @@ -35792,6 +36233,7 @@ void flecs_rest_parse_json_ser_iter_params( flecs_rest_bool_param(req, "colors", &desc->serialize_colors); flecs_rest_bool_param(req, "duration", &desc->measure_eval_duration); flecs_rest_bool_param(req, "type_info", &desc->serialize_type_info); + flecs_rest_bool_param(req, "serialize_table", &desc->serialize_table); } static @@ -35822,6 +36264,17 @@ bool flecs_rest_reply_entity( return true; } +static +bool flecs_rest_reply_world( + ecs_world_t *world, + const ecs_http_request_t* req, + ecs_http_reply_t *reply) +{ + (void)req; + ecs_world_to_json_buf(world, &reply->body); + return true; +} + static ecs_entity_t flecs_rest_entity_from_path( ecs_world_t *world, @@ -36433,6 +36886,10 @@ bool flecs_rest_reply( } else if (!ecs_os_strcmp(req->path, "query")) { return flecs_rest_reply_query(world, impl, req, reply); + /* World endpoint */ + } else if (!ecs_os_strcmp(req->path, "world")) { + return flecs_rest_reply_world(world, req, reply); + /* Stats endpoint */ } else if (!ecs_os_strncmp(req->path, "stats/", 6)) { return flecs_rest_reply_stats(world, req, reply); @@ -37200,6 +37657,9 @@ void http_enqueue_request( req->pub.conn = (ecs_http_connection_t*)conn; req->pub.method = frag->method; req->pub.path = res + 1; + + http_decode_url_str(req->pub.path); + if (frag->body_offset) { req->pub.body = &res[frag->body_offset]; } @@ -40217,148 +40677,148 @@ const ecs_id_t ECS_TOGGLE = (1ull << 61); const ecs_id_t ECS_AND = (1ull << 60); /** Builtin component ids */ -const ecs_entity_t ecs_id(EcsComponent) = 1; -const ecs_entity_t ecs_id(EcsIdentifier) = 2; -const ecs_entity_t ecs_id(EcsIterable) = 3; -const ecs_entity_t ecs_id(EcsPoly) = 4; +const ecs_entity_t ecs_id(EcsComponent) = 1; +const ecs_entity_t ecs_id(EcsIdentifier) = 2; +const ecs_entity_t ecs_id(EcsIterable) = 3; +const ecs_entity_t ecs_id(EcsPoly) = 4; -const ecs_entity_t EcsQuery = 5; -const ecs_entity_t EcsObserver = 7; - -/* System module component ids */ -const ecs_entity_t EcsSystem = 10; -const ecs_entity_t ecs_id(EcsTickSource) = 11; - -/** Timer module component ids */ -const ecs_entity_t ecs_id(EcsTimer) = 13; -const ecs_entity_t ecs_id(EcsRateFilter) = 14; - -/** Meta module component ids */ -const ecs_entity_t ecs_id(EcsMetaType) = 15; -const ecs_entity_t ecs_id(EcsMetaTypeSerialized) = 16; -const ecs_entity_t ecs_id(EcsPrimitive) = 17; -const ecs_entity_t ecs_id(EcsEnum) = 18; -const ecs_entity_t ecs_id(EcsBitmask) = 19; -const ecs_entity_t ecs_id(EcsMember) = 20; -const ecs_entity_t ecs_id(EcsStruct) = 21; -const ecs_entity_t ecs_id(EcsArray) = 22; -const ecs_entity_t ecs_id(EcsVector) = 23; -const ecs_entity_t ecs_id(EcsUnit) = 24; -const ecs_entity_t ecs_id(EcsUnitPrefix) = 25; +/* Poly target components */ +const ecs_entity_t EcsQuery = 5; +const ecs_entity_t EcsObserver = 6; +const ecs_entity_t EcsSystem = 7; /* Core scopes & entities */ -const ecs_entity_t EcsWorld = ECS_HI_COMPONENT_ID + 0; -const ecs_entity_t EcsFlecs = ECS_HI_COMPONENT_ID + 1; -const ecs_entity_t EcsFlecsCore = ECS_HI_COMPONENT_ID + 2; -const ecs_entity_t EcsFlecsInternals = ECS_HI_COMPONENT_ID + 3; -const ecs_entity_t EcsModule = ECS_HI_COMPONENT_ID + 4; -const ecs_entity_t EcsPrivate = ECS_HI_COMPONENT_ID + 5; -const ecs_entity_t EcsPrefab = ECS_HI_COMPONENT_ID + 6; -const ecs_entity_t EcsDisabled = ECS_HI_COMPONENT_ID + 7; +const ecs_entity_t EcsWorld = ECS_HI_COMPONENT_ID + 0; +const ecs_entity_t EcsFlecs = ECS_HI_COMPONENT_ID + 1; +const ecs_entity_t EcsFlecsCore = ECS_HI_COMPONENT_ID + 2; +const ecs_entity_t EcsFlecsInternals = ECS_HI_COMPONENT_ID + 3; +const ecs_entity_t EcsModule = ECS_HI_COMPONENT_ID + 4; +const ecs_entity_t EcsPrivate = ECS_HI_COMPONENT_ID + 5; +const ecs_entity_t EcsPrefab = ECS_HI_COMPONENT_ID + 6; +const ecs_entity_t EcsDisabled = ECS_HI_COMPONENT_ID + 7; -const ecs_entity_t EcsSlotOf = ECS_HI_COMPONENT_ID + 8; -const ecs_entity_t EcsFlag = ECS_HI_COMPONENT_ID + 9; +const ecs_entity_t EcsSlotOf = ECS_HI_COMPONENT_ID + 8; +const ecs_entity_t EcsFlag = ECS_HI_COMPONENT_ID + 9; /* Relationship properties */ -const ecs_entity_t EcsWildcard = ECS_HI_COMPONENT_ID + 10; -const ecs_entity_t EcsAny = ECS_HI_COMPONENT_ID + 11; -const ecs_entity_t EcsThis = ECS_HI_COMPONENT_ID + 12; -const ecs_entity_t EcsVariable = ECS_HI_COMPONENT_ID + 13; +const ecs_entity_t EcsWildcard = ECS_HI_COMPONENT_ID + 10; +const ecs_entity_t EcsAny = ECS_HI_COMPONENT_ID + 11; +const ecs_entity_t EcsThis = ECS_HI_COMPONENT_ID + 12; +const ecs_entity_t EcsVariable = ECS_HI_COMPONENT_ID + 13; -const ecs_entity_t EcsTransitive = ECS_HI_COMPONENT_ID + 14; -const ecs_entity_t EcsReflexive = ECS_HI_COMPONENT_ID + 15; -const ecs_entity_t EcsSymmetric = ECS_HI_COMPONENT_ID + 16; -const ecs_entity_t EcsFinal = ECS_HI_COMPONENT_ID + 17; -const ecs_entity_t EcsDontInherit = ECS_HI_COMPONENT_ID + 18; -const ecs_entity_t EcsTag = ECS_HI_COMPONENT_ID + 19; -const ecs_entity_t EcsUnion = ECS_HI_COMPONENT_ID + 20; -const ecs_entity_t EcsExclusive = ECS_HI_COMPONENT_ID + 21; -const ecs_entity_t EcsAcyclic = ECS_HI_COMPONENT_ID + 22; -const ecs_entity_t EcsWith = ECS_HI_COMPONENT_ID + 23; -const ecs_entity_t EcsOneOf = ECS_HI_COMPONENT_ID + 24; +const ecs_entity_t EcsTransitive = ECS_HI_COMPONENT_ID + 14; +const ecs_entity_t EcsReflexive = ECS_HI_COMPONENT_ID + 15; +const ecs_entity_t EcsSymmetric = ECS_HI_COMPONENT_ID + 16; +const ecs_entity_t EcsFinal = ECS_HI_COMPONENT_ID + 17; +const ecs_entity_t EcsDontInherit = ECS_HI_COMPONENT_ID + 18; +const ecs_entity_t EcsTag = ECS_HI_COMPONENT_ID + 19; +const ecs_entity_t EcsUnion = ECS_HI_COMPONENT_ID + 20; +const ecs_entity_t EcsExclusive = ECS_HI_COMPONENT_ID + 21; +const ecs_entity_t EcsAcyclic = ECS_HI_COMPONENT_ID + 22; +const ecs_entity_t EcsWith = ECS_HI_COMPONENT_ID + 23; +const ecs_entity_t EcsOneOf = ECS_HI_COMPONENT_ID + 24; /* Builtin relationships */ -const ecs_entity_t EcsChildOf = ECS_HI_COMPONENT_ID + 25; -const ecs_entity_t EcsIsA = ECS_HI_COMPONENT_ID + 26; -const ecs_entity_t EcsDependsOn = ECS_HI_COMPONENT_ID + 27; +const ecs_entity_t EcsChildOf = ECS_HI_COMPONENT_ID + 25; +const ecs_entity_t EcsIsA = ECS_HI_COMPONENT_ID + 26; +const ecs_entity_t EcsDependsOn = ECS_HI_COMPONENT_ID + 27; /* Identifier tags */ -const ecs_entity_t EcsName = ECS_HI_COMPONENT_ID + 30; -const ecs_entity_t EcsSymbol = ECS_HI_COMPONENT_ID + 31; -const ecs_entity_t EcsAlias = ECS_HI_COMPONENT_ID + 32; +const ecs_entity_t EcsName = ECS_HI_COMPONENT_ID + 30; +const ecs_entity_t EcsSymbol = ECS_HI_COMPONENT_ID + 31; +const ecs_entity_t EcsAlias = ECS_HI_COMPONENT_ID + 32; /* Events */ -const ecs_entity_t EcsOnAdd = ECS_HI_COMPONENT_ID + 33; -const ecs_entity_t EcsOnRemove = ECS_HI_COMPONENT_ID + 34; -const ecs_entity_t EcsOnSet = ECS_HI_COMPONENT_ID + 35; -const ecs_entity_t EcsUnSet = ECS_HI_COMPONENT_ID + 36; -const ecs_entity_t EcsOnDelete = ECS_HI_COMPONENT_ID + 37; -const ecs_entity_t EcsOnCreateTable = ECS_HI_COMPONENT_ID + 38; -const ecs_entity_t EcsOnDeleteTable = ECS_HI_COMPONENT_ID + 39; -const ecs_entity_t EcsOnTableEmpty = ECS_HI_COMPONENT_ID + 40; -const ecs_entity_t EcsOnTableFill = ECS_HI_COMPONENT_ID + 41; -const ecs_entity_t EcsOnCreateTrigger = ECS_HI_COMPONENT_ID + 42; -const ecs_entity_t EcsOnDeleteTrigger = ECS_HI_COMPONENT_ID + 43; -const ecs_entity_t EcsOnDeleteObservable = ECS_HI_COMPONENT_ID + 44; -const ecs_entity_t EcsOnComponentHooks = ECS_HI_COMPONENT_ID + 45; -const ecs_entity_t EcsOnDeleteTarget = ECS_HI_COMPONENT_ID + 46; +const ecs_entity_t EcsOnAdd = ECS_HI_COMPONENT_ID + 33; +const ecs_entity_t EcsOnRemove = ECS_HI_COMPONENT_ID + 34; +const ecs_entity_t EcsOnSet = ECS_HI_COMPONENT_ID + 35; +const ecs_entity_t EcsUnSet = ECS_HI_COMPONENT_ID + 36; +const ecs_entity_t EcsOnDelete = ECS_HI_COMPONENT_ID + 37; +const ecs_entity_t EcsOnTableCreate = ECS_HI_COMPONENT_ID + 38; +const ecs_entity_t EcsOnTableDelete = ECS_HI_COMPONENT_ID + 39; +const ecs_entity_t EcsOnTableEmpty = ECS_HI_COMPONENT_ID + 40; +const ecs_entity_t EcsOnTableFill = ECS_HI_COMPONENT_ID + 41; +const ecs_entity_t EcsOnCreateTrigger = ECS_HI_COMPONENT_ID + 42; +const ecs_entity_t EcsOnDeleteTrigger = ECS_HI_COMPONENT_ID + 43; +const ecs_entity_t EcsOnDeleteObservable = ECS_HI_COMPONENT_ID + 44; +const ecs_entity_t EcsOnComponentHooks = ECS_HI_COMPONENT_ID + 45; +const ecs_entity_t EcsOnDeleteTarget = ECS_HI_COMPONENT_ID + 46; + +/* Timers */ +const ecs_entity_t ecs_id(EcsTickSource) = ECS_HI_COMPONENT_ID + 47; +const ecs_entity_t ecs_id(EcsTimer) = ECS_HI_COMPONENT_ID + 48; +const ecs_entity_t ecs_id(EcsRateFilter) = ECS_HI_COMPONENT_ID + 49; /* Actions */ -const ecs_entity_t EcsRemove = ECS_HI_COMPONENT_ID + 50; -const ecs_entity_t EcsDelete = ECS_HI_COMPONENT_ID + 51; -const ecs_entity_t EcsPanic = ECS_HI_COMPONENT_ID + 52; +const ecs_entity_t EcsRemove = ECS_HI_COMPONENT_ID + 50; +const ecs_entity_t EcsDelete = ECS_HI_COMPONENT_ID + 51; +const ecs_entity_t EcsPanic = ECS_HI_COMPONENT_ID + 52; /* Misc */ -const ecs_entity_t EcsDefaultChildComponent = ECS_HI_COMPONENT_ID + 55; +const ecs_entity_t EcsDefaultChildComponent = ECS_HI_COMPONENT_ID + 55; /* Systems */ -const ecs_entity_t EcsMonitor = ECS_HI_COMPONENT_ID + 61; -const ecs_entity_t EcsEmpty = ECS_HI_COMPONENT_ID + 62; -const ecs_entity_t ecs_id(EcsPipeline) = ECS_HI_COMPONENT_ID + 63; -const ecs_entity_t EcsOnStart = ECS_HI_COMPONENT_ID + 64; -const ecs_entity_t EcsPreFrame = ECS_HI_COMPONENT_ID + 65; -const ecs_entity_t EcsOnLoad = ECS_HI_COMPONENT_ID + 66; -const ecs_entity_t EcsPostLoad = ECS_HI_COMPONENT_ID + 67; -const ecs_entity_t EcsPreUpdate = ECS_HI_COMPONENT_ID + 68; -const ecs_entity_t EcsOnUpdate = ECS_HI_COMPONENT_ID + 69; -const ecs_entity_t EcsOnValidate = ECS_HI_COMPONENT_ID + 70; -const ecs_entity_t EcsPostUpdate = ECS_HI_COMPONENT_ID + 71; -const ecs_entity_t EcsPreStore = ECS_HI_COMPONENT_ID + 72; -const ecs_entity_t EcsOnStore = ECS_HI_COMPONENT_ID + 73; -const ecs_entity_t EcsPostFrame = ECS_HI_COMPONENT_ID + 74; +const ecs_entity_t EcsMonitor = ECS_HI_COMPONENT_ID + 61; +const ecs_entity_t EcsEmpty = ECS_HI_COMPONENT_ID + 62; +const ecs_entity_t ecs_id(EcsPipeline) = ECS_HI_COMPONENT_ID + 63; +const ecs_entity_t EcsOnStart = ECS_HI_COMPONENT_ID + 64; +const ecs_entity_t EcsPreFrame = ECS_HI_COMPONENT_ID + 65; +const ecs_entity_t EcsOnLoad = ECS_HI_COMPONENT_ID + 66; +const ecs_entity_t EcsPostLoad = ECS_HI_COMPONENT_ID + 67; +const ecs_entity_t EcsPreUpdate = ECS_HI_COMPONENT_ID + 68; +const ecs_entity_t EcsOnUpdate = ECS_HI_COMPONENT_ID + 69; +const ecs_entity_t EcsOnValidate = ECS_HI_COMPONENT_ID + 70; +const ecs_entity_t EcsPostUpdate = ECS_HI_COMPONENT_ID + 71; +const ecs_entity_t EcsPreStore = ECS_HI_COMPONENT_ID + 72; +const ecs_entity_t EcsOnStore = ECS_HI_COMPONENT_ID + 73; +const ecs_entity_t EcsPostFrame = ECS_HI_COMPONENT_ID + 74; -const ecs_entity_t EcsPhase = ECS_HI_COMPONENT_ID + 75; +const ecs_entity_t EcsPhase = ECS_HI_COMPONENT_ID + 75; /* Meta primitive components (don't use low ids to save id space) */ -const ecs_entity_t ecs_id(ecs_bool_t) = ECS_HI_COMPONENT_ID + 80; -const ecs_entity_t ecs_id(ecs_char_t) = ECS_HI_COMPONENT_ID + 81; -const ecs_entity_t ecs_id(ecs_byte_t) = ECS_HI_COMPONENT_ID + 82; -const ecs_entity_t ecs_id(ecs_u8_t) = ECS_HI_COMPONENT_ID + 83; -const ecs_entity_t ecs_id(ecs_u16_t) = ECS_HI_COMPONENT_ID + 84; -const ecs_entity_t ecs_id(ecs_u32_t) = ECS_HI_COMPONENT_ID + 85; -const ecs_entity_t ecs_id(ecs_u64_t) = ECS_HI_COMPONENT_ID + 86; -const ecs_entity_t ecs_id(ecs_uptr_t) = ECS_HI_COMPONENT_ID + 87; -const ecs_entity_t ecs_id(ecs_i8_t) = ECS_HI_COMPONENT_ID + 88; -const ecs_entity_t ecs_id(ecs_i16_t) = ECS_HI_COMPONENT_ID + 89; -const ecs_entity_t ecs_id(ecs_i32_t) = ECS_HI_COMPONENT_ID + 90; -const ecs_entity_t ecs_id(ecs_i64_t) = ECS_HI_COMPONENT_ID + 91; -const ecs_entity_t ecs_id(ecs_iptr_t) = ECS_HI_COMPONENT_ID + 92; -const ecs_entity_t ecs_id(ecs_f32_t) = ECS_HI_COMPONENT_ID + 93; -const ecs_entity_t ecs_id(ecs_f64_t) = ECS_HI_COMPONENT_ID + 94; -const ecs_entity_t ecs_id(ecs_string_t) = ECS_HI_COMPONENT_ID + 95; -const ecs_entity_t ecs_id(ecs_entity_t) = ECS_HI_COMPONENT_ID + 96; -const ecs_entity_t EcsConstant = ECS_HI_COMPONENT_ID + 97; -const ecs_entity_t EcsQuantity = ECS_HI_COMPONENT_ID + 98; +const ecs_entity_t ecs_id(ecs_bool_t) = ECS_HI_COMPONENT_ID + 80; +const ecs_entity_t ecs_id(ecs_char_t) = ECS_HI_COMPONENT_ID + 81; +const ecs_entity_t ecs_id(ecs_byte_t) = ECS_HI_COMPONENT_ID + 82; +const ecs_entity_t ecs_id(ecs_u8_t) = ECS_HI_COMPONENT_ID + 83; +const ecs_entity_t ecs_id(ecs_u16_t) = ECS_HI_COMPONENT_ID + 84; +const ecs_entity_t ecs_id(ecs_u32_t) = ECS_HI_COMPONENT_ID + 85; +const ecs_entity_t ecs_id(ecs_u64_t) = ECS_HI_COMPONENT_ID + 86; +const ecs_entity_t ecs_id(ecs_uptr_t) = ECS_HI_COMPONENT_ID + 87; +const ecs_entity_t ecs_id(ecs_i8_t) = ECS_HI_COMPONENT_ID + 88; +const ecs_entity_t ecs_id(ecs_i16_t) = ECS_HI_COMPONENT_ID + 89; +const ecs_entity_t ecs_id(ecs_i32_t) = ECS_HI_COMPONENT_ID + 90; +const ecs_entity_t ecs_id(ecs_i64_t) = ECS_HI_COMPONENT_ID + 91; +const ecs_entity_t ecs_id(ecs_iptr_t) = ECS_HI_COMPONENT_ID + 92; +const ecs_entity_t ecs_id(ecs_f32_t) = ECS_HI_COMPONENT_ID + 93; +const ecs_entity_t ecs_id(ecs_f64_t) = ECS_HI_COMPONENT_ID + 94; +const ecs_entity_t ecs_id(ecs_string_t) = ECS_HI_COMPONENT_ID + 95; +const ecs_entity_t ecs_id(ecs_entity_t) = ECS_HI_COMPONENT_ID + 96; + +/** Meta module component ids */ +const ecs_entity_t ecs_id(EcsMetaType) = ECS_HI_COMPONENT_ID + 97; +const ecs_entity_t ecs_id(EcsMetaTypeSerialized) = ECS_HI_COMPONENT_ID + 98; +const ecs_entity_t ecs_id(EcsPrimitive) = ECS_HI_COMPONENT_ID + 99; +const ecs_entity_t ecs_id(EcsEnum) = ECS_HI_COMPONENT_ID + 100; +const ecs_entity_t ecs_id(EcsBitmask) = ECS_HI_COMPONENT_ID + 101; +const ecs_entity_t ecs_id(EcsMember) = ECS_HI_COMPONENT_ID + 102; +const ecs_entity_t ecs_id(EcsStruct) = ECS_HI_COMPONENT_ID + 103; +const ecs_entity_t ecs_id(EcsArray) = ECS_HI_COMPONENT_ID + 104; +const ecs_entity_t ecs_id(EcsVector) = ECS_HI_COMPONENT_ID + 105; +const ecs_entity_t ecs_id(EcsOpaque) = ECS_HI_COMPONENT_ID + 106; +const ecs_entity_t ecs_id(EcsUnit) = ECS_HI_COMPONENT_ID + 107; +const ecs_entity_t ecs_id(EcsUnitPrefix) = ECS_HI_COMPONENT_ID + 108; +const ecs_entity_t EcsConstant = ECS_HI_COMPONENT_ID + 109; +const ecs_entity_t EcsQuantity = ECS_HI_COMPONENT_ID + 110; /* Doc module components */ -const ecs_entity_t ecs_id(EcsDocDescription) =ECS_HI_COMPONENT_ID + 100; -const ecs_entity_t EcsDocBrief = ECS_HI_COMPONENT_ID + 101; -const ecs_entity_t EcsDocDetail = ECS_HI_COMPONENT_ID + 102; -const ecs_entity_t EcsDocLink = ECS_HI_COMPONENT_ID + 103; -const ecs_entity_t EcsDocColor = ECS_HI_COMPONENT_ID + 104; +const ecs_entity_t ecs_id(EcsDocDescription) = ECS_HI_COMPONENT_ID + 111; +const ecs_entity_t EcsDocBrief = ECS_HI_COMPONENT_ID + 112; +const ecs_entity_t EcsDocDetail = ECS_HI_COMPONENT_ID + 113; +const ecs_entity_t EcsDocLink = ECS_HI_COMPONENT_ID + 114; +const ecs_entity_t EcsDocColor = ECS_HI_COMPONENT_ID + 115; /* REST module components */ -const ecs_entity_t ecs_id(EcsRest) = ECS_HI_COMPONENT_ID + 105; +const ecs_entity_t ecs_id(EcsRest) = ECS_HI_COMPONENT_ID + 116; /* Default lookup path */ static ecs_entity_t ecs_default_lookup_path[2] = { 0, 0 }; @@ -41850,47 +42310,6 @@ const ecs_world_info_t* ecs_get_world_info( return &world->info; } -void flecs_notify_queries( - ecs_world_t *world, - ecs_query_event_t *event) -{ - ecs_poly_assert(world, ecs_world_t); - - ecs_id_record_t *idr = flecs_id_record_get(world, - ecs_pair(ecs_id(EcsPoly), EcsQuery)); - if (!idr) { - return; - } - - ecs_table_cache_iter_t it; - const ecs_table_record_t *tr; - if (flecs_table_cache_all_iter(&idr->cache, &it)) { - while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) { - ecs_table_t *table = tr->hdr.table; - int32_t i, count = ecs_table_count(table); - if (!count) { - continue; - } - - EcsPoly *queries = ecs_table_get_column(table, tr->column, 0); - for (i = 0; i < count; i ++) { - ecs_query_t *query = queries[i].poly; - if (!ecs_poly_is(query, ecs_query_t)) { - /* EcsQuery can also contain filters or rules */ - continue; - } - - if (query->flags & EcsQueryIsSubquery) { - continue; - } - - ecs_poly_assert(query, ecs_query_t); - flecs_query_notify(world, query, event); - } - } - } -} - void flecs_delete_table( ecs_world_t *world, ecs_table_t *table) @@ -46685,35 +47104,36 @@ void flecs_unregister_observer( } } - static bool flecs_ignore_observer( ecs_world_t *world, ecs_observer_t *observer, ecs_table_t *table) { + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(observer != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); + int32_t *last_event_id = observer->last_event_id; if (last_event_id && last_event_id[0] == world->event_id) { return true; } - if (!table) { - return false; - } - - ecs_filter_t *filter = &observer->filter; - if (!(filter->flags & EcsFilterMatchPrefab) && - (table->flags & EcsTableIsPrefab)) - { - return true; - } - if (!(filter->flags & EcsFilterMatchDisabled) && - (table->flags & EcsTableIsDisabled)) - { - return true; - } - - return false; + ecs_flags32_t table_flags = table->flags, filter_flags = observer->filter.flags; + + bool result = (table_flags & EcsTableIsPrefab) && + !(filter_flags & EcsFilterMatchPrefab); + result = result || ((table_flags & EcsTableIsDisabled) && + !(filter_flags & EcsFilterMatchDisabled)); + + return result; +} + +static +bool flecs_is_simple_result( + ecs_iter_t *it) +{ + return (it->count == 1) || (it->sizes[0] == 0) || (it->sources[0] == 0); } static @@ -46722,12 +47142,11 @@ void flecs_observer_invoke( ecs_iter_t *it, ecs_observer_t *observer, ecs_iter_action_t callback, - ecs_table_t *table, - int32_t term_index) + int32_t term_index, + bool simple_result) { ecs_assert(it->callback != NULL, ECS_INVALID_PARAMETER, NULL); - ecs_table_lock(it->world, table); if (ecs_should_log_3()) { char *path = ecs_get_fullpath(world, it->system); ecs_dbg_3("observer: invoke %s", path); @@ -46741,27 +47160,24 @@ void flecs_observer_invoke( ecs_filter_t *filter = &observer->filter; ecs_assert(term_index < filter->term_count, ECS_INTERNAL_ERROR, NULL); ecs_term_t *term = &filter->terms[term_index]; - ecs_entity_t observer_src = term->src.id; - if (observer_src && !(term->src.flags & EcsIsEntity)) { - observer_src = 0; - } - if (term->oper != EcsNot) { - ecs_assert((it->offset + it->count) <= ecs_table_count(table), + ecs_assert((it->offset + it->count) <= ecs_table_count(it->table), ECS_INTERNAL_ERROR, NULL); } - int32_t i, count = it->count; - ecs_size_t size = it->sizes[0]; - ecs_entity_t src = it->sources[0]; bool instanced = filter->flags & EcsFilterIsInstanced; - - if (!observer_src && ((count == 1) || (size == 0) || (src == 0) || instanced)) { - ECS_BIT_COND(it->flags, EcsIterIsInstanced, instanced); + bool match_this = filter->flags & EcsFilterMatchThis; + if (match_this && (simple_result || instanced)) { callback(it); } else { - ECS_BIT_CLEAR(it->flags, EcsIterIsInstanced); + ecs_entity_t observer_src = term->src.id; + if (observer_src && !(term->src.flags & EcsIsEntity)) { + observer_src = 0; + } + ecs_entity_t *entities = it->entities; + int32_t i, count = it->count; + ecs_entity_t src = it->sources[0]; it->count = 1; for (i = 0; i < count; i ++) { ecs_entity_t e = entities[i]; @@ -46784,7 +47200,24 @@ void flecs_observer_invoke( } ecs_log_pop_3(); - ecs_table_unlock(it->world, table); +} + +static +void flecs_default_uni_observer_run_callback(ecs_iter_t *it) { + ecs_observer_t *o = it->ctx; + it->ctx = o->ctx; + it->callback = o->callback; + + if (ecs_should_log_3()) { + char *path = ecs_get_fullpath(it->world, it->system); + ecs_dbg_3("observer %s", path); + ecs_os_free(path); + } + + ecs_log_push_3(); + flecs_observer_invoke(it->real_world, it, o, o->callback, 0, + flecs_is_simple_result(it)); + ecs_log_pop_3(); } static @@ -46793,7 +47226,8 @@ void flecs_uni_observer_invoke( ecs_observer_t *observer, ecs_iter_t *it, ecs_table_t *table, - ecs_entity_t trav) + ecs_entity_t trav, + bool simple_result) { ecs_filter_t *filter = &observer->filter; ecs_term_t *term = &filter->terms[0]; @@ -46802,7 +47236,6 @@ void flecs_uni_observer_invoke( } ecs_assert(trav == 0 || it->sources[0] != 0, ECS_INTERNAL_ERROR, NULL); - if (trav && term->src.trav != trav) { return; } @@ -46817,10 +47250,6 @@ void flecs_uni_observer_invoke( ecs_entity_t event = it->event; it->event = flecs_get_observer_event(term, event); - void *ptrs = it->ptrs; - if (is_filter) { - it->ptrs = NULL; - } if (observer->run) { it->next = flecs_default_observer_next_callback; @@ -46830,11 +47259,10 @@ void flecs_uni_observer_invoke( } else { ecs_iter_action_t callback = observer->callback; it->callback = callback; - flecs_observer_invoke(world, it, observer, callback, table, 0); + flecs_observer_invoke(world, it, observer, callback, 0, simple_result); } it->event = event; - it->ptrs = ptrs; } void flecs_observers_invoke( @@ -46845,12 +47273,17 @@ void flecs_observers_invoke( ecs_entity_t trav) { if (ecs_map_is_init(observers)) { + ecs_table_lock(it->world, table); + + bool simple_result = flecs_is_simple_result(it); ecs_map_iter_t oit = ecs_map_iter(observers); while (ecs_map_next(&oit)) { ecs_observer_t *o = ecs_map_ptr(&oit); ecs_assert(it->table == table, ECS_INTERNAL_ERROR, NULL); - flecs_uni_observer_invoke(world, o, it, table, trav); + flecs_uni_observer_invoke(world, o, it, table, trav, simple_result); } + + ecs_table_unlock(it->world, table); } } @@ -46945,7 +47378,10 @@ bool flecs_multi_observer_invoke(ecs_iter_t *it) { user_it.callback = o->callback; flecs_iter_validate(&user_it); - flecs_observer_invoke(world, &user_it, o, o->callback, table, pivot_term); + ecs_table_lock(it->world, table); + flecs_observer_invoke(world, &user_it, o, o->callback, + pivot_term, flecs_is_simple_result(&user_it)); + ecs_table_unlock(it->world, table); ecs_iter_fini(&user_it); return true; } @@ -46961,7 +47397,10 @@ bool ecs_observer_default_run_action(ecs_iter_t *it) { return flecs_multi_observer_invoke(it); } else { it->ctx = o->ctx; - flecs_observer_invoke(it->real_world, it, o, o->callback, it->table, 0); + ecs_table_lock(it->world, it->table); + flecs_observer_invoke(it->real_world, it, o, o->callback, 0, + flecs_is_simple_result(it)); + ecs_table_unlock(it->world, it->table); return true; } } @@ -46971,22 +47410,6 @@ void flecs_default_multi_observer_run_callback(ecs_iter_t *it) { flecs_multi_observer_invoke(it); } -void flecs_default_uni_observer_run_callback(ecs_iter_t *it) { - ecs_observer_t *o = it->ctx; - it->ctx = o->ctx; - it->callback = o->callback; - - if (ecs_should_log_3()) { - char *path = ecs_get_fullpath(it->world, it->system); - ecs_dbg_3("observer %s", path); - ecs_os_free(path); - } - - ecs_log_push_3(); - flecs_observer_invoke(it->real_world, it, o, o->callback, it->table, 0); - ecs_log_pop_3(); -} - /* For convenience, so applications can (in theory) use a single run callback * that uses ecs_iter_next to iterate results */ bool flecs_default_observer_next_callback(ecs_iter_t *it) { @@ -47105,6 +47528,9 @@ int flecs_uni_observer_init( { ecs_term_t *term = &observer->filter.terms[0]; observer->last_event_id = desc->last_event_id; + if (!observer->last_event_id) { + observer->last_event_id = &observer->last_event_id_storage; + } observer->register_id = flecs_from_public_id(world, term->id); term->field_index = desc->term_index; @@ -47412,7 +47838,6 @@ void flecs_observer_fini( ecs_observer_t *observer) { if (observer->is_multi) { - /* Child observers get deleted up by entity cleanup logic */ ecs_os_free(observer->last_event_id); } else { if (observer->filter.term_count) { @@ -47627,30 +48052,17 @@ void* ecs_table_cache_get( void* ecs_table_cache_remove( ecs_table_cache_t *cache, - const ecs_table_t *table, + uint64_t table_id, ecs_table_cache_hdr_t *elem) { ecs_assert(cache != NULL, ECS_INTERNAL_ERROR, NULL); - ecs_assert(table != NULL, ECS_INTERNAL_ERROR, NULL); - - if (!ecs_map_is_init(&cache->index)) { - return NULL; - } - - if (!elem) { - elem = ecs_map_get_deref(&cache->index, - ecs_table_cache_hdr_t, table->id); - if (!elem) { - return false; - } - } - + ecs_assert(table_id != 0, ECS_INTERNAL_ERROR, NULL); ecs_assert(elem != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(elem->cache == cache, ECS_INTERNAL_ERROR, NULL); - ecs_assert(elem->table == table, ECS_INTERNAL_ERROR, NULL); flecs_table_cache_list_remove(cache, elem); - ecs_map_remove(&cache->index, table->id); + ecs_map_remove(&cache->index, table_id); return elem; } @@ -48088,6 +48500,12 @@ char* ecs_os_api_strdup(const char *str) { } } +void ecs_os_strset(char **str, const char *value) { + char *old = str[0]; + str[0] = ecs_os_strdup(value); + ecs_os_free(old); +} + /* Replace dots with underscores */ static char *module_file_base(const char *module, char sep) { @@ -49091,6 +49509,22 @@ void flecs_query_set_table_match( } } +static +ecs_query_table_t* flecs_query_table_insert( + ecs_world_t *world, + ecs_query_t *query, + ecs_table_t *table) +{ + ecs_query_table_t *qt = flecs_bcalloc(&world->allocators.query_table); + if (table) { + qt->table_id = table->id; + } else { + qt->table_id = 0; + } + ecs_table_cache_insert(&query->cache, table, &qt->hdr); + return qt; +} + /** Populate query cache with tables */ static void flecs_query_match_tables( @@ -49108,9 +49542,8 @@ void flecs_query_match_tables( while (ecs_filter_next(&it)) { if ((table != it.table) || (!it.table && !qt)) { /* New table matched, add record to cache */ - qt = flecs_bcalloc(&world->allocators.query_table); - ecs_table_cache_insert(&query->cache, it.table, &qt->hdr); table = it.table; + qt = flecs_query_table_insert(world, query, table); } ecs_query_table_match_t *qm = flecs_query_add_table_match(query, qt, table); @@ -49143,9 +49576,8 @@ bool flecs_query_match_table( while (ecs_filter_next(&it)) { ecs_assert(it.table == table, ECS_INTERNAL_ERROR, NULL); if (qt == NULL) { - qt = flecs_bcalloc(&world->allocators.query_table); - ecs_table_cache_insert(&query->cache, it.table, &qt->hdr); table = it.table; + qt = flecs_query_table_insert(world, query, table); } ecs_query_table_match_t *qm = flecs_query_add_table_match(query, qt, table); @@ -49716,12 +50148,15 @@ void flecs_query_table_free( static void flecs_query_unmatch_table( ecs_query_t *query, - ecs_table_t *table) + ecs_table_t *table, + ecs_query_table_t *elem) { - ecs_query_table_t *qt = ecs_table_cache_remove( - &query->cache, table, NULL); - if (qt) { - flecs_query_table_free(query, qt); + if (!elem) { + elem = ecs_table_cache_get(&query->cache, table); + } + if (elem) { + ecs_table_cache_remove(&query->cache, elem->table_id, &elem->hdr); + flecs_query_table_free(query, elem); } } @@ -49773,8 +50208,7 @@ void flecs_query_rematch_tables( qt = ecs_table_cache_get(&query->cache, table); if (!qt) { - qt = flecs_bcalloc(&world->allocators.query_table); - ecs_table_cache_insert(&query->cache, table, &qt->hdr); + qt = flecs_query_table_insert(world, query, table); } ecs_assert(qt->hdr.table == table, ECS_INTERNAL_ERROR, NULL); @@ -49811,7 +50245,7 @@ void flecs_query_rematch_tables( if (flecs_table_cache_all_iter(&query->cache, &cache_it)) { while ((qt = flecs_table_cache_next(&cache_it, ecs_query_table_t))) { if (qt->rematch_count != rematch_count) { - flecs_query_unmatch_table(query, qt->hdr.table); + flecs_query_unmatch_table(query, qt->hdr.table, qt); } } } @@ -49863,7 +50297,7 @@ void flecs_query_notify( break; case EcsQueryTableUnmatch: /* Deletion of table */ - flecs_query_unmatch_table(query, event->table); + flecs_query_unmatch_table(query, event->table, NULL); break; case EcsQueryTableRematch: /* Rematch tables of query */ @@ -49967,6 +50401,23 @@ void flecs_query_on_event( ecs_query_t *query = o->ctx; ecs_table_t *table = it->table; + ecs_entity_t event = it->event; + + if (event == EcsOnTableCreate) { + /* Creation of new table */ + if (flecs_query_match_table(world, query, table)) { + if (query->subqueries) { + ecs_query_event_t evt = { + .kind = EcsQueryTableMatch, + .table = table, + .parent_query = query + }; + flecs_query_notify_subqueries(world, query, &evt); + } + } + return; + } + ecs_assert(query != NULL, ECS_INTERNAL_ERROR, NULL); /* The observer isn't doing the matching because the query can do it more @@ -49975,12 +50426,23 @@ void flecs_query_on_event( return; } - ecs_entity_t event = it->event; if (event == EcsOnTableEmpty) { flecs_query_update_table(query, table, true); } else if (event == EcsOnTableFill) { flecs_query_update_table(query, table, false); + } else if (event == EcsOnTableDelete) { + /* Deletion of table */ + flecs_query_unmatch_table(query, table, NULL); + if (query->subqueries) { + ecs_query_event_t evt = { + .kind = EcsQueryTableUnmatch, + .table = table, + .parent_query = query + }; + flecs_query_notify_subqueries(world, query, &evt); + } + return; } } @@ -50112,6 +50574,10 @@ ecs_query_t* ecs_query_init( observer_desc.ctx = result; observer_desc.events[0] = EcsOnTableEmpty; observer_desc.events[1] = EcsOnTableFill; + if (!desc->parent) { + observer_desc.events[2] = EcsOnTableCreate; + observer_desc.events[3] = EcsOnTableDelete; + } observer_desc.filter.flags |= EcsFilterNoData; observer_desc.filter.instanced = true; @@ -51312,9 +51778,12 @@ ecs_table_t *flecs_create_table( flecs_init_table(world, result, prev); - flecs_notify_queries(world, &(ecs_query_event_t) { - .kind = EcsQueryTableMatch, - .table = result + flecs_emit(world, world, &(ecs_event_desc_t) { + .ids = &result->type, + .event = EcsOnTableCreate, + .table = result, + .flags = EcsEventTableOnly, + .observable = world }); /* Update counters */ @@ -53878,7 +54347,7 @@ void flecs_register_symmetric(ecs_iter_t *it) { /* Create observer that adds the reverse relationship when R(X, Y) is * added, or remove the reverse relationship when R(X, Y) is removed. */ ecs_observer_init(world, &(ecs_observer_desc_t){ - .entity = ecs_entity(world, {.add = {ecs_childof(EcsFlecsInternals)}}), + .entity = ecs_entity(world, {.add = {ecs_childof(r)}}), .filter.terms[0] = { .id = ecs_pair(r, EcsWildcard) }, .callback = flecs_on_symmetric_add_remove, .events = {EcsOnAdd, EcsOnRemove} @@ -53933,13 +54402,10 @@ void flecs_on_parent_change(ecs_iter_t *it) { ecs_world_t *world = it->world; ecs_table_t *other_table = it->other_table, *table = it->table; - int32_t col = ecs_search(it->real_world, table, - ecs_pair(ecs_id(EcsIdentifier), EcsName), 0); - bool has_name = col != -1; - bool other_has_name = ecs_search(it->real_world, other_table, - ecs_pair(ecs_id(EcsIdentifier), EcsName), 0) != -1; - - if (!has_name && !other_has_name) { + EcsIdentifier *names = ecs_table_get_pair(it->real_world, + table, EcsIdentifier, EcsName, it->offset); + bool has_name = names != NULL; + if (!has_name) { /* If tables don't have names, index does not need to be updated */ return; } @@ -53951,6 +54417,8 @@ void flecs_on_parent_change(ecs_iter_t *it) { ecs_search(it->real_world, other_table, ecs_pair(EcsChildOf, EcsWildcard), &from_pair); + bool other_has_name = ecs_search(it->real_world, other_table, + ecs_pair(ecs_id(EcsIdentifier), EcsName), 0) != -1; bool to_has_name = has_name, from_has_name = other_has_name; if (it->event == EcsOnRemove) { if (from_pair != ecs_childof(0)) { @@ -53970,10 +54438,6 @@ void flecs_on_parent_change(ecs_iter_t *it) { from_has_name = has_name; } - /* Get the table column with names */ - EcsIdentifier *names = ecs_table_get_pair(it->real_world, - table, EcsIdentifier, EcsName, it->offset); - ecs_hashmap_t *from_index = 0; if (from_has_name) { from_index = flecs_id_name_index_get(world, from_pair); @@ -53995,6 +54459,7 @@ void flecs_on_parent_change(ecs_iter_t *it) { const char *name_str = name->value; if (to_index && name_str) { ecs_assert(name->hash != 0, ECS_INTERNAL_ERROR, NULL); + flecs_name_index_ensure( to_index, e, name_str, name->length, name->hash); name->index = to_index; @@ -54303,6 +54768,8 @@ void flecs_bootstrap( flecs_bootstrap_entity(world, EcsOnSet, "OnSet", EcsFlecsCore); flecs_bootstrap_entity(world, EcsUnSet, "UnSet", EcsFlecsCore); flecs_bootstrap_entity(world, EcsMonitor, "EcsMonitor", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsOnTableCreate, "OnTableCreate", EcsFlecsCore); + flecs_bootstrap_entity(world, EcsOnTableDelete, "OnTableDelete", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableEmpty, "OnTableEmpty", EcsFlecsCore); flecs_bootstrap_entity(world, EcsOnTableFill, "OnTableFilled", EcsFlecsCore); @@ -55434,8 +55901,12 @@ void flecs_id_record_free( ecs_id_t id = idr->id; flecs_id_record_assert_empty(idr); - ecs_assert((world->flags & EcsWorldQuit) || (idr->keep_alive == 0), - ECS_ID_IN_USE, "cannot delete id that is in use"); + + if (!(world->flags & EcsWorldQuit)) { + /* Id is still in use by a filter, query, rule or observer */ + ecs_assert((idr->keep_alive == 0), + ECS_ID_IN_USE, "cannot delete id that is queried for"); + } if (ECS_IS_PAIR(id)) { ecs_entity_t rel = ecs_pair_first(world, id); diff --git a/code/vendors/flecs/flecs.h b/code/vendors/flecs/flecs.h index c696f17..f3897a8 100644 --- a/code/vendors/flecs/flecs.h +++ b/code/vendors/flecs/flecs.h @@ -2576,8 +2576,6 @@ void ecs_os_set_api_defaults(void); #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)) @@ -2686,6 +2684,9 @@ void ecs_os_fatal(const char *file, int32_t line, const char *msg); FLECS_API const char* ecs_os_strerror(int err); +FLECS_API +void ecs_os_strset(char **str, const char *value); + #ifdef FLECS_ACCURATE_COUNTERS #define ecs_os_inc(v) (ecs_os_ainc(v)) #define ecs_os_linc(v) (ecs_os_lainc(v)) @@ -3168,6 +3169,7 @@ struct ecs_observer_t { ecs_observable_t *observable; /**< Observable for observer */ int32_t *last_event_id; /**< Last handled event id */ + int32_t last_event_id_storage; ecs_id_t register_id; /**< Id observer is registered with (single term observers only) */ int32_t term_index; /**< Index of the term in parent observer (single term observers only) */ @@ -4380,10 +4382,10 @@ FLECS_API extern const ecs_entity_t EcsMonitor; FLECS_API extern const ecs_entity_t EcsOnDelete; /** Event. Triggers when a table is created. */ -// FLECS_API extern const ecs_entity_t EcsOnCreateTable; +FLECS_API extern const ecs_entity_t EcsOnTableCreate; /** Event. Triggers when a table is deleted. */ -// FLECS_API extern const ecs_entity_t EcsOnDeleteTable; +FLECS_API extern const ecs_entity_t EcsOnTableDelete; /** Event. Triggers when a table becomes empty (doesn't emit on creation). */ FLECS_API extern const ecs_entity_t EcsOnTableEmpty; @@ -4440,7 +4442,7 @@ FLECS_API extern const ecs_entity_t EcsPhase; /** The first user-defined component starts from this id. Ids up to this number * are reserved for builtin components */ -#define EcsFirstUserComponentId (32) +#define EcsFirstUserComponentId (8) /** The first user-defined entity starts from this id. Ids up to this number * are reserved for builtin components */ @@ -5506,12 +5508,13 @@ void ecs_ref_update( ecs_ref_t *ref); /** 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. + * This operation returns a mutable pointer to a component. If the component did + * not yet exist, it will be added. + * + * If get_mut is called when the world is in deferred/readonly mode, the + * function will: + * - return a pointer to a temp storage if the component does not yet exist, or + * - return a pointer to the existing component if it exists * * @param world The world. * @param entity The entity. @@ -8439,6 +8442,17 @@ int ecs_value_move_ctor( #define ecs_entity(world, ...)\ ecs_entity_init(world, &(ecs_entity_desc_t) __VA_ARGS__ ) +/** Shorthand for creating a component with ecs_component_init. + * + * Example: + * ecs_component(world, { + * .type.size = 4, + * .type.alignment = 4 + * }); + */ +#define ecs_component(world, ...)\ + ecs_component_init(world, &(ecs_component_desc_t) __VA_ARGS__ ) + /** Shorthand for creating a filter with ecs_filter_init. * * Example: @@ -8996,6 +9010,11 @@ int ecs_value_move_ctor( #endif // FLECS_C_ + +#ifdef __cplusplus +} +#endif + /** * @file addons.h * @brief Include enabled addons. @@ -11698,6 +11717,7 @@ typedef struct ecs_iter_to_json_desc_t { bool serialize_colors; /**< Serialize doc color for entities */ bool measure_eval_duration; /**< Serialize evaluation duration */ bool serialize_type_info; /**< Serialize type information */ + bool serialize_table; /**< Serialize entire table vs. matched components */ } ecs_iter_to_json_desc_t; #define ECS_ITER_TO_JSON_INIT (ecs_iter_to_json_desc_t){\ @@ -11714,7 +11734,8 @@ typedef struct ecs_iter_to_json_desc_t { .serialize_variable_ids = false, \ .serialize_colors = false, \ .measure_eval_duration = false, \ - .serialize_type_info = false \ + .serialize_type_info = false, \ + .serialize_table = false \ } /** Serialize iterator into JSON string. @@ -11746,6 +11767,37 @@ int ecs_iter_to_json_buf( ecs_strbuf_t *buf_out, const ecs_iter_to_json_desc_t *desc); +/** Serialize world into JSON string. + * This operation iterates the contents of the world to JSON. The operation is + * equivalent to the following code: + * + * ecs_filter_t *f = ecs_filter(world, { + * .terms = {{ .id = EcsAny }} + * }); + * + * ecs_iter_t it = ecs_filter_init(world, &f); + * ecs_iter_to_json_desc_t desc = { .serialize_table = true }; + * ecs_iter_to_json(world, iter, &desc); + * + * @param world The world to serialize. + * @return A JSON string with the serialized iterator data, or NULL if failed. + */ +FLECS_API +char* ecs_world_to_json( + ecs_world_t *world); + +/** Serialize world into JSON string buffer. + * Same as ecs_world_to_json, but serializes to an ecs_strbuf_t instance. + * + * @param world The world to serialize. + * @param buf_out The strbuf to append the string to. + * @return Zero if success, non-zero if failed. + */ +FLECS_API +int ecs_world_to_json_buf( + ecs_world_t *world, + ecs_strbuf_t *buf_out); + #ifdef __cplusplus } #endif @@ -12229,6 +12281,7 @@ FLECS_API extern const ecs_entity_t ecs_id(EcsMember); FLECS_API extern const ecs_entity_t ecs_id(EcsStruct); FLECS_API extern const ecs_entity_t ecs_id(EcsArray); FLECS_API extern const ecs_entity_t ecs_id(EcsVector); +FLECS_API extern const ecs_entity_t ecs_id(EcsOpaque); FLECS_API extern const ecs_entity_t ecs_id(EcsUnit); FLECS_API extern const ecs_entity_t ecs_id(EcsUnitPrefix); FLECS_API extern const ecs_entity_t EcsConstant; @@ -12261,6 +12314,7 @@ typedef enum ecs_type_kind_t { EcsStructType, EcsArrayType, EcsVectorType, + EcsOpaqueType, EcsTypeKindLast = EcsVectorType } ecs_type_kind_t; @@ -12371,6 +12425,72 @@ typedef struct EcsVector { } EcsVector; +/* Opaque type support */ + +#if !defined(__cplusplus) || !defined(FLECS_CPP) + +/** Serializer interface */ +typedef struct ecs_meta_serializer_t { + /* Serialize value */ + int (*value)( + const struct ecs_meta_serializer_t *ser, /**< Serializer */ + ecs_entity_t type, /**< Type of the value to serialize */ + const void *value); /**< Pointer to the value to serialize */ + + /* Serialize member */ + int (*member)( + const struct ecs_meta_serializer_t *ser, /**< Serializer */ + const char *member); /**< Member name */ + + const ecs_world_t *world; + void *ctx; +} ecs_meta_serializer_t; + +#elif defined(__cplusplus) + +} /* extern "C" { */ + +/** Serializer interface (same layout as C, but with convenience methods) */ +typedef struct ecs_meta_serializer_t { + /* Serialize value */ + int (*value_)( + const struct ecs_meta_serializer_t *ser, + ecs_entity_t type, + const void *value); + + /* Serialize member */ + int (*member_)( + const struct ecs_meta_serializer_t *ser, + const char *name); + + /* Serialize value */ + int value(ecs_entity_t type, const void *value) const; + + /* Serialize value */ + template + int value(const T& value) const; + + /* Serialize member */ + int member(const char *name) const; + + const ecs_world_t *world; + void *ctx; +} ecs_meta_serializer_t; + +extern "C" { +#endif + +/** Callback invoked serializing an opaque type. */ +typedef int (*ecs_meta_serialize_t)( + const ecs_meta_serializer_t *ser, + const void *src); /**< Pointer to value to serialize */ + +typedef struct EcsOpaque { + ecs_entity_t as_type; /**< Type that describes the serialized output */ + ecs_meta_serialize_t serialize; /**< Serialize action */ +} EcsOpaque; + + /* Units */ /* Helper type to describe translation between two units. Note that this @@ -12404,6 +12524,7 @@ typedef struct EcsUnitPrefix { typedef enum ecs_meta_type_op_kind_t { EcsOpArray, EcsOpVector, + EcsOpOpaque, EcsOpPush, EcsOpPop, @@ -12655,8 +12776,7 @@ ecs_entity_t ecs_meta_get_entity( /** Used with ecs_primitive_init. */ typedef struct ecs_primitive_desc_t { - /* Existing entity to associate with primitive (optional) */ - ecs_entity_t entity; + ecs_entity_t entity; /**< Existing entity to use for type (optional) */ ecs_primitive_kind_t kind; } ecs_primitive_desc_t; @@ -12668,8 +12788,7 @@ ecs_entity_t ecs_primitive_init( /** Used with ecs_enum_init. */ typedef struct ecs_enum_desc_t { - /* Existing entity to associate with enum (optional) */ - ecs_entity_t entity; + ecs_entity_t entity; /**< Existing entity to use for type (optional) */ ecs_enum_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; } ecs_enum_desc_t; @@ -12682,8 +12801,7 @@ ecs_entity_t ecs_enum_init( /** Used with ecs_bitmask_init. */ typedef struct ecs_bitmask_desc_t { - /* Existing entity to associate with bitmask (optional) */ - ecs_entity_t entity; + ecs_entity_t entity; /**< Existing entity to use for type (optional) */ ecs_bitmask_constant_t constants[ECS_MEMBER_DESC_CACHE_SIZE]; } ecs_bitmask_desc_t; @@ -12696,8 +12814,7 @@ ecs_entity_t ecs_bitmask_init( /** Used with ecs_array_init. */ typedef struct ecs_array_desc_t { - /* Existing entity to associate with array (optional) */ - ecs_entity_t entity; + ecs_entity_t entity; /**< Existing entity to use for type (optional) */ ecs_entity_t type; int32_t count; } ecs_array_desc_t; @@ -12711,8 +12828,7 @@ ecs_entity_t ecs_array_init( /** Used with ecs_vector_init. */ typedef struct ecs_vector_desc_t { - /* Existing entity to associate with vector (optional) */ - ecs_entity_t entity; + ecs_entity_t entity; /**< Existing entity to use for type (optional) */ ecs_entity_t type; } ecs_vector_desc_t; @@ -12725,8 +12841,7 @@ ecs_entity_t ecs_vector_init( /** Used with ecs_struct_init. */ typedef struct ecs_struct_desc_t { - /* Existing entity to associate with struct (optional) */ - ecs_entity_t entity; + ecs_entity_t entity; /**< Existing entity to use for type (optional) */ ecs_member_t members[ECS_MEMBER_DESC_CACHE_SIZE]; } ecs_struct_desc_t; @@ -12736,6 +12851,36 @@ ecs_entity_t ecs_struct_init( ecs_world_t *world, const ecs_struct_desc_t *desc); +/** Used with ecs_opaque_init. */ +typedef struct ecs_opaque_desc_t { + ecs_entity_t entity; + ecs_entity_t as_type; /**< Type that describes the serialized output */ + ecs_meta_serialize_t serialize; /**< Serialize action */ +} ecs_opaque_desc_t; + +/** Create a new opaque type. + * Opaque types are types of which the layout doesn't match what can be modelled + * with the primitives of the meta framework, but which have a structure + * that can be described with meta primitives. Typical examples are STL types + * such as std::string or std::vector, types with a nontrivial layout, and types + * that only expose getter/setter methods. + * + * An opaque type is a combination of a serialization function, and a handle to + * a meta type which describes the structure of the serialized output. For + * example, an opaque type for std::string would have a serializer function that + * accesses .c_str(), and with type ecs_string_t. + * + * The serializer callback accepts a serializer object and a pointer to the + * value of the opaque type to be serialized. The serializer has two methods: + * + * - value, which serializes a value (such as .c_str()) + * - member, which specifies a member to be serialized (in the case of a struct) + */ +FLECS_API +ecs_entity_t ecs_opaque_init( + ecs_world_t *world, + const ecs_opaque_desc_t *desc); + /** Used with ecs_unit_init. */ typedef struct ecs_unit_desc_t { /** Existing entity to associate with unit (optional) */ @@ -12812,6 +12957,9 @@ ecs_entity_t ecs_quantity_init( #define ecs_vector(world, ...)\ ecs_vector_init(world, &(ecs_vector_desc_t) __VA_ARGS__ ) +#define ecs_opaque(world, ...)\ + ecs_opaque_init(world, &(ecs_opaque_desc_t) __VA_ARGS__ ) + #define ecs_struct(world, ...)\ ecs_struct_init(world, &(ecs_struct_desc_t) __VA_ARGS__ ) @@ -14364,8 +14512,6 @@ int32_t ecs_cpp_reset_count_inc(void); #ifdef __cplusplus -} - /** * @file addons/cpp/flecs.hpp * @brief Flecs C++11 API. @@ -14493,6 +14639,8 @@ 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; +static const flecs::entity_t OnTableCreate = EcsOnTableCreate; +static const flecs::entity_t OnTableDelete = EcsOnTableDelete; /* Builtin term flags */ static const uint32_t Self = EcsSelf; @@ -16064,6 +16212,7 @@ namespace flecs { * @{ */ +/* Primitive type aliases */ using bool_t = ecs_bool_t; using char_t = ecs_char_t; using u8_t = ecs_u8_t; @@ -16079,12 +16228,12 @@ using iptr_t = ecs_iptr_t; using f32_t = ecs_f32_t; using f64_t = ecs_f64_t; -using type_kind_t = ecs_type_kind_t; -using primitive_kind_t = ecs_primitive_kind_t; +/* Embedded type aliases */ using member_t = ecs_member_t; using enum_constant_t = ecs_enum_constant_t; using bitmask_constant_t = ecs_bitmask_constant_t; +/* Components */ using MetaType = EcsMetaType; using MetaTypeSerialized = EcsMetaTypeSerialized; using Primitive = EcsPrimitive; @@ -16101,6 +16250,7 @@ struct bitmask { uint32_t value; }; +/* Handles to builtin reflection types */ static const flecs::entity_t Bool = ecs_id(ecs_bool_t); static const flecs::entity_t Char = ecs_id(ecs_char_t); static const flecs::entity_t Byte = ecs_id(ecs_byte_t); @@ -16118,12 +16268,53 @@ static const flecs::entity_t F32 = ecs_id(ecs_f32_t); static const flecs::entity_t F64 = ecs_id(ecs_f64_t); static const flecs::entity_t String = ecs_id(ecs_string_t); static const flecs::entity_t Entity = ecs_id(ecs_entity_t); - static const flecs::entity_t Constant = EcsConstant; static const flecs::entity_t Quantity = EcsQuantity; +/** Serializer object, used for serializing opaque types */ +using serializer = ecs_meta_serializer_t; + +/** Serializer function, used to serialize opaque types */ +using serialize_t = ecs_meta_serialize_t; + +/** Type safe variant of serializer function */ +template +using serialize = int(*)(const serializer *, const T*); + namespace meta { +/* Type kinds supported by reflection system */ +using type_kind_t = ecs_type_kind_t; +static const type_kind_t PrimitiveType = EcsPrimitiveType; +static const type_kind_t BitmaskType = EcsBitmaskType; +static const type_kind_t EnumType = EcsEnumType; +static const type_kind_t StructType = EcsStructType; +static const type_kind_t ArrayType = EcsArrayType; +static const type_kind_t VectorType = EcsVectorType; +static const type_kind_t CustomType = EcsOpaqueType; +static const type_kind_t TypeKindLast = EcsTypeKindLast; + +/* Primitive type kinds supported by reflection system */ +using primitive_kind_t = ecs_primitive_kind_t; +static const primitive_kind_t Bool = EcsBool; +static const primitive_kind_t Char = EcsChar; +static const primitive_kind_t Byte = EcsByte; +static const primitive_kind_t U8 = EcsU8; +static const primitive_kind_t U16 = EcsU16; +static const primitive_kind_t U32 = EcsU32; +static const primitive_kind_t U64 = EcsU64; +static const primitive_kind_t I8 = EcsI8; +static const primitive_kind_t I16 = EcsI16; +static const primitive_kind_t I32 = EcsI32; +static const primitive_kind_t I64 = EcsI64; +static const primitive_kind_t F32 = EcsF32; +static const primitive_kind_t F64 = EcsF64; +static const primitive_kind_t UPtr = EcsUPtr; +static const primitive_kind_t IPtr = EcsIPtr; +static const primitive_kind_t String = EcsString; +static const primitive_kind_t Entity = EcsEntity; +static const primitive_kind_t PrimitiveKindLast = EcsPrimitiveKindLast; + /** Class for reading/writing dynamic values. * * \ingroup cpp_addons_meta @@ -17519,7 +17710,7 @@ template ::value > = 0> inline void set(world_t *world, flecs::entity_t entity, T&& value, flecs::id_t id) { ecs_assert(_::cpp_type::size() != 0, ECS_INVALID_PARAMETER, NULL); - T& dst = *static_cast(ecs_get_mut_id(world, entity, id)); + T& dst = *static_cast*>(ecs_get_mut_id(world, entity, id)); dst = FLECS_MOV(value); @@ -18903,27 +19094,48 @@ int plecs_from_file(const char *filename) const { * @{ */ +/** Convert value to string */ flecs::string to_expr(flecs::entity_t tid, const void* value) { char *expr = ecs_ptr_to_expr(m_world, tid, value); return flecs::string(expr); } +/** Convert value to string */ template flecs::string to_expr(const T* value) { flecs::entity_t tid = _::cpp_type::id(m_world); return to_expr(tid, value); } +/** Return meta cursor to value */ flecs::meta::cursor cursor(flecs::entity_t tid, void *ptr) { return flecs::meta::cursor(m_world, tid, ptr); } +/** Return meta cursor to value */ template flecs::meta::cursor cursor(void *ptr) { flecs::entity_t tid = _::cpp_type::id(m_world); return cursor(tid, ptr); } +/** Create primitive type */ +flecs::entity primitive(flecs::meta::primitive_kind_t kind); + +/** Create array type. */ +flecs::entity array(flecs::entity_t elem_id, int32_t array_count); + +/** Create array type. */ +template +flecs::entity array(int32_t array_count); + +/** Create vector type. */ +flecs::entity vector(flecs::entity_t elem_id); + +/** Create vector type. */ +template +flecs::entity vector(); + /** @} */ # endif @@ -18954,6 +19166,15 @@ flecs::string to_json(const T* value) { return to_json(tid, value); } +/** Serialize world to JSON. + * + * \memberof flecs::world + * \ingroup cpp_addons_json + */ +flecs::string to_json() { + return flecs::string( ecs_world_to_json(m_world) ); +} + # endif # ifdef FLECS_APP /** @@ -23138,6 +23359,7 @@ untyped_component& bit(const char *name, uint32_t value) { } /** @} */ + # endif }; @@ -23278,6 +23500,22 @@ struct component : untyped_component { return *this; } +# ifdef FLECS_META + +/** Register custom reflection function for component. */ +component& serialize(flecs::id_t as_type, flecs::serialize ser) { + ecs_opaque_desc_t desc = {}; + + /* Safe cast, from a function with a T* arg to a void* arg */ + desc.serialize = reinterpret_cast(ser); + desc.entity = m_id; + desc.as_type = as_type; + ecs_opaque_init(m_world, &desc); + return *this; +} + +# endif + private: using BindingCtx = _::component_binding_ctx; @@ -23733,7 +23971,7 @@ inline flecs::entity id::second() const { if (m_world) { return flecs::entity(m_world, ecs_get_alive(m_world, e)); } else { - return flecs::entity(m_world, e); + return flecs::entity(e); } } @@ -27086,8 +27324,8 @@ inline rule_base::operator rule<>() const { #pragma once -FLECS_ENUM_LAST(flecs::type_kind_t, EcsTypeKindLast) -FLECS_ENUM_LAST(flecs::primitive_kind_t, EcsPrimitiveKindLast) +FLECS_ENUM_LAST(flecs::meta::type_kind_t, flecs::meta::TypeKindLast) +FLECS_ENUM_LAST(flecs::meta::primitive_kind_t, flecs::meta::PrimitiveKindLast) namespace flecs { namespace meta { @@ -27163,8 +27401,61 @@ inline flecs::entity cursor::get_entity() const { } } // namespace meta + +/** Create primitive type */ +inline flecs::entity world::primitive(flecs::meta::primitive_kind_t kind) { + ecs_primitive_desc_t desc = {}; + desc.kind = kind; + flecs::entity_t eid = ecs_primitive_init(m_world, &desc); + ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); + return flecs::entity(m_world, eid); +} + +/** Create array type. */ +inline flecs::entity world::array(flecs::entity_t elem_id, int32_t array_count) { + ecs_array_desc_t desc = {}; + desc.type = elem_id; + desc.count = array_count; + flecs::entity_t eid = ecs_array_init(m_world, &desc); + ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); + return flecs::entity(m_world, eid); +} + +/** Create array type. */ +template +inline flecs::entity world::array(int32_t array_count) { + return this->array(_::cpp_type::id(m_world), array_count); +} + +inline flecs::entity world::vector(flecs::entity_t elem_id) { + ecs_vector_desc_t desc = {}; + desc.type = elem_id; + flecs::entity_t eid = ecs_vector_init(m_world, &desc); + ecs_assert(eid != 0, ECS_INVALID_OPERATION, NULL); + return flecs::entity(m_world, eid); +} + +template +inline flecs::entity world::vector() { + return this->vector(_::cpp_type::id()); +} + } // namespace flecs +inline int ecs_meta_serializer_t::value(ecs_entity_t type, const void *v) const { + return this->value_(this, type, v); +} + +template +inline int ecs_meta_serializer_t::value(const T& v) const { + return this->value(flecs::_::cpp_type::id( + const_cast(this->world)), &v); +} + +inline int ecs_meta_serializer_t::member(const char *name) const { + return this->member_(this, name); +} + #endif #ifdef FLECS_UNITS /** @@ -27693,8 +27984,6 @@ inline flecs::scoped_world world::scope() const { /** @} */ - -extern "C" { #endif // __cplusplus #endif // FLECS_CPP @@ -27702,9 +27991,5 @@ extern "C" { #endif -#ifdef __cplusplus -} -#endif - #endif