diff --git a/distr/flecs.c b/distr/flecs.c index a277b96272..334fac4345 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -85722,10 +85722,10 @@ bool flecs_query_trav_fixed_src_up_fixed_second( /* Check if table has transitive relationship by traversing upwards */ ecs_table_record_t *tr = NULL; - ecs_search_relation(ctx->world, table, 0, - ecs_pair(trav, second), trav, EcsSelf|EcsUp, NULL, NULL, &tr); + const int32_t index = ecs_search_relation(ctx->world, table, 0, + ecs_pair(trav, second), trav, EcsSelf| EcsUp, NULL, NULL, &tr); - if (!tr) { + if (index == -1 || !tr) { if (op->match_flags & EcsTermReflexive) { return flecs_query_trav_fixed_src_reflexive(op, ctx, &range, trav, second); @@ -85734,7 +85734,17 @@ bool flecs_query_trav_fixed_src_up_fixed_second( } } - flecs_query_set_trav_match(op, tr, trav, second, ctx); + ecs_id_t matched = ecs_pair(trav, second); + if (trav == EcsIsA) { + matched = table->type.array[index]; + } + + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + it->ids [op->field_index] = matched; + flecs_query_it_set_tr(it, op->field_index, tr); + } + flecs_query_set_vars(op, matched, ctx); return true; } diff --git a/docs/Docs.md b/docs/Docs.md index deb2472b77..b175554105 100644 --- a/docs/Docs.md +++ b/docs/Docs.md @@ -7,7 +7,7 @@ - [Getting Started with Unreal Engine](https://github.com/PreyK/Unreal-Minimum-Viable-Flecs) - [Unreal-Flecs (Unreal Engine Flecs plugin)](https://github.com/Reddy-dev/Unreal-Flecs) - [Godot-flecs-sample (example)](https://github.com/paulfigiel/godot-flecs-sample) -- [Flecs-Godot (GDExtension)](https://github.com/VoxInteractive/Godot-Flecs) +- [Stagehand (GDExtension)](https://github.com/VerdantInteractive/Stagehand) - [Godot-turbo (engine module)](https://github.com/callmefloof/godot-turbo/) ([demo project](https://github.com/callmefloof/godot_turbo_demo_projects)) - [Getting Started with Raylib](https://github.com/kranzky/raylib-flecs-starter-kit) - [Getting Started with SDL3, ImGui, Sqlite](https://github.com/ilyas-taouaou/CodotakuCMakeRepo) ([video](https://www.youtube.com/watch?v=T32B7nf6B-8)) diff --git a/src/query/engine/eval_trav.c b/src/query/engine/eval_trav.c index 00177c982f..460080362e 100644 --- a/src/query/engine/eval_trav.c +++ b/src/query/engine/eval_trav.c @@ -96,10 +96,10 @@ bool flecs_query_trav_fixed_src_up_fixed_second( /* Check if table has transitive relationship by traversing upwards */ ecs_table_record_t *tr = NULL; - ecs_search_relation(ctx->world, table, 0, - ecs_pair(trav, second), trav, EcsSelf|EcsUp, NULL, NULL, &tr); + const int32_t index = ecs_search_relation(ctx->world, table, 0, + ecs_pair(trav, second), trav, EcsSelf| EcsUp, NULL, NULL, &tr); - if (!tr) { + if (index == -1 || !tr) { if (op->match_flags & EcsTermReflexive) { return flecs_query_trav_fixed_src_reflexive(op, ctx, &range, trav, second); @@ -108,7 +108,17 @@ bool flecs_query_trav_fixed_src_up_fixed_second( } } - flecs_query_set_trav_match(op, tr, trav, second, ctx); + ecs_id_t matched = ecs_pair(trav, second); + if (trav == EcsIsA) { + matched = table->type.array[index]; + } + + if (op->field_index != -1) { + ecs_iter_t *it = ctx->it; + it->ids [op->field_index] = matched; + flecs_query_it_set_tr(it, op->field_index, tr); + } + flecs_query_set_vars(op, matched, ctx); return true; } diff --git a/test/query/project.json b/test/query/project.json index 2e814e63ad..3f3387558d 100644 --- a/test/query/project.json +++ b/test/query/project.json @@ -1690,7 +1690,8 @@ "this_or_w_self_up_childof", "this_or_w_self_up_childof_2", "this_or_w_self_up_childof_w_tag", - "this_written_or_w_self_up_childof" + "this_written_or_w_self_up_childof", + "this_written_up_isa_trav_id_n_lvl" ] }, { "id": "Cascade", diff --git a/test/query/src/Traversal.c b/test/query/src/Traversal.c index 90c4a987e0..64648a0edf 100644 --- a/test/query/src/Traversal.c +++ b/test/query/src/Traversal.c @@ -13160,3 +13160,49 @@ void Traversal_this_written_or_w_self_up_childof(void) { ecs_fini(world); } + +void Traversal_this_written_up_isa_trav_id_n_lvl(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + + /* Setup: instance --(IsA)--> troll --(IsA)--> enemy + * Position is on troll and inherited by instance. + * Querying (IsA, enemy) on this should match instance and report + * the field ID as (IsA, troll) — the actual pair in the archetype — + * not (IsA, enemy) which was incorrectly reconstructed from arguments + * before the fix in flecs_query_trav_fixed_src_up_fixed_second. */ + ecs_entity_t enemy = ecs_new_w_id(world, EcsPrefab); + ecs_entity_t troll = ecs_new_w_id(world, EcsPrefab); + ecs_add_pair(world, troll, EcsIsA, enemy); + ecs_set(world, troll, Position, {10, 20}); + + ecs_entity_t instance = ecs_new_w_pair(world, EcsIsA, troll); + + ecs_query_t *q = ecs_query(world, { + .terms = { + { ecs_id(Position) }, /* constrains 'this' to tables with Position */ + { .id = ecs_pair(EcsIsA, enemy) } /* transitive IsA check */ + }, + .cache_kind = cache_kind + }); + + test_assert(q != NULL); + + ecs_iter_t it = ecs_query_iter(world, q); + test_bool(true, ecs_query_next(&it)); + test_int(1, it.count); + test_uint(instance, it.entities[0]); + test_uint(ecs_id(Position), ecs_field_id(&it, 0)); + test_uint(troll, ecs_field_src(&it, 0)); + /* The field ID for the (IsA, enemy) term must be the actual pair stored + * in the instance's archetype: (IsA, troll), not the query target. */ + test_uint(ecs_pair(EcsIsA, troll), ecs_field_id(&it, 1)); + + test_bool(false, ecs_query_next(&it)); + + ecs_query_fini(q); + + ecs_fini(world); +}